Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python3.4 |
| 2 | # |
| 3 | # Copyright 2019 - 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 | |
| 17 | import collections |
| 18 | import json |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 19 | import math |
| 20 | import os |
| 21 | import time |
| 22 | from acts import asserts |
| 23 | from acts import base_test |
| 24 | from acts import context |
Omar El Ayach | d710909 | 2019-09-29 18:31:33 -0700 | [diff] [blame] | 25 | from acts import utils |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 26 | from acts.controllers import iperf_server as ipf |
| 27 | from acts.controllers.utils_lib import ssh |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 28 | from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 29 | from acts.test_utils.wifi import wifi_performance_test_utils as wputils |
| 30 | from acts.test_utils.wifi import wifi_retail_ap as retail_ap |
| 31 | from acts.test_utils.wifi import wifi_test_utils as wutils |
| 32 | |
| 33 | SHORT_SLEEP = 1 |
| 34 | MED_SLEEP = 5 |
| 35 | TRAFFIC_GAP_THRESH = 0.5 |
| 36 | IPERF_INTERVAL = 0.25 |
| 37 | |
| 38 | |
| 39 | class WifiRoamingPerformanceTest(base_test.BaseTestClass): |
| 40 | """Class for ping-based Wifi performance tests. |
| 41 | |
| 42 | This class implements WiFi ping performance tests such as range and RTT. |
| 43 | The class setups up the AP in the desired configurations, configures |
| 44 | and connects the phone to the AP, and runs For an example config file to |
| 45 | run this test class see example_connectivity_performance_ap_sta.json. |
| 46 | """ |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 47 | def __init__(self, controllers): |
| 48 | base_test.BaseTestClass.__init__(self, controllers) |
| 49 | self.testcase_metric_logger = ( |
| 50 | BlackboxMappedMetricLogger.for_test_case()) |
| 51 | self.testclass_metric_logger = ( |
| 52 | BlackboxMappedMetricLogger.for_test_class()) |
| 53 | self.publish_testcase_metrics = True |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 54 | |
| 55 | def setup_class(self): |
| 56 | """Initializes common test hardware and parameters. |
| 57 | |
| 58 | This function initializes hardwares and compiles parameters that are |
| 59 | common to all tests in this class. |
| 60 | """ |
Omar El Ayach | 74f78dc | 2019-07-30 15:15:34 -0700 | [diff] [blame] | 61 | self.dut = self.android_devices[-1] |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 62 | req_params = [ |
| 63 | 'RetailAccessPoints', 'roaming_test_params', 'testbed_params' |
| 64 | ] |
| 65 | opt_params = ['main_network', 'RemoteServer'] |
| 66 | self.unpack_userparams(req_params, opt_params) |
| 67 | self.testclass_params = self.roaming_test_params |
| 68 | self.num_atten = self.attenuators[0].instrument.num_atten |
| 69 | self.remote_server = ssh.connection.SshConnection( |
| 70 | ssh.settings.from_config(self.RemoteServer[0]['ssh_config'])) |
| 71 | self.remote_server.setup_master_ssh() |
| 72 | self.iperf_server = self.iperf_servers[0] |
| 73 | self.iperf_client = self.iperf_clients[0] |
| 74 | self.access_point = retail_ap.create(self.RetailAccessPoints)[0] |
| 75 | self.log.info('Access Point Configuration: {}'.format( |
| 76 | self.access_point.ap_settings)) |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 77 | |
Omar El Ayach | 9c56cf3 | 2019-09-19 13:07:51 -0700 | [diff] [blame] | 78 | if hasattr(self, 'bdf'): |
| 79 | self.log.info('Pushing WiFi BDF to DUT.') |
| 80 | wputils.push_bdf(self.dut, self.bdf) |
| 81 | if hasattr(self, 'firmware'): |
| 82 | self.log.info('Pushing WiFi firmware to DUT.') |
| 83 | wlanmdsp = [ |
| 84 | file for file in self.firmware if "wlanmdsp.mbn" in file |
| 85 | ][0] |
| 86 | data_msc = [file for file in self.firmware |
| 87 | if "Data.msc" in file][0] |
| 88 | wputils.push_firmware(self.dut, wlanmdsp, data_msc) |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 89 | # Get RF connection map |
| 90 | self.log.info("Getting RF connection map.") |
Omar El Ayach | 98be238 | 2019-09-04 15:11:50 -0700 | [diff] [blame] | 91 | wutils.wifi_toggle_state(self.dut, True) |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 92 | self.rf_map_by_network, self.rf_map_by_atten = ( |
| 93 | wputils.get_full_rf_connection_map(self.attenuators, self.dut, |
| 94 | self.remote_server, |
| 95 | self.main_network)) |
Omar El Ayach | ab047c0 | 2019-09-03 11:38:30 -0700 | [diff] [blame] | 96 | self.log.info("RF Map (by Network): {}".format(self.rf_map_by_network)) |
| 97 | self.log.info("RF Map (by Atten): {}".format(self.rf_map_by_atten)) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 98 | |
| 99 | #Turn WiFi ON |
Omar El Ayach | d710909 | 2019-09-29 18:31:33 -0700 | [diff] [blame] | 100 | if self.testclass_params.get('airplane_mode', 1): |
| 101 | self.log.info('Turning on airplane mode.') |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 102 | asserts.assert_true(utils.force_airplane_mode(self.dut, True), |
| 103 | "Can not turn on airplane mode.") |
Omar El Ayach | 9c56cf3 | 2019-09-19 13:07:51 -0700 | [diff] [blame] | 104 | wutils.wifi_toggle_state(self.dut, True) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 105 | |
| 106 | def pass_fail_traffic_continuity(self, result): |
| 107 | """Pass fail check for traffic continuity |
| 108 | |
| 109 | Currently, the function only reports test results and implicitly passes |
| 110 | the test. A pass fail criterion is current being researched. |
| 111 | |
| 112 | Args: |
| 113 | result: dict containing test results |
| 114 | """ |
| 115 | self.log.info('Detected {} roam transitions:'.format( |
| 116 | len(result['roam_transitions']))) |
| 117 | for event in result['roam_transitions']: |
| 118 | self.log.info('Roam: {} -> {})'.format(event[0], event[1])) |
| 119 | self.log.info('Roam transition statistics: {}'.format( |
| 120 | result['roam_counts'])) |
| 121 | |
| 122 | formatted_traffic_gaps = [ |
| 123 | round(gap, 2) for gap in result['traffic_disruption'] |
| 124 | ] |
| 125 | self.log.info('Detected {} traffic gaps of duration: {}'.format( |
| 126 | len(result['traffic_disruption']), formatted_traffic_gaps)) |
| 127 | |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 128 | if result['total_roams'] > 0: |
| 129 | disruption_percentage = (len(result['traffic_disruption']) / |
| 130 | result['total_roams']) * 100 |
| 131 | max_disruption = max(result['traffic_disruption']) |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 132 | else: |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 133 | disruption_percentage = 0 |
| 134 | max_disruption = 0 |
| 135 | self.testcase_metric_logger.add_metric('disruption_percentage', |
| 136 | disruption_percentage) |
| 137 | self.testcase_metric_logger.add_metric('max_disruption', |
| 138 | max_disruption) |
| 139 | |
| 140 | if disruption_percentage == 0: |
| 141 | asserts.explicit_pass('Test passed. No traffic disruptions found.') |
| 142 | elif max_disruption > self.testclass_params[ |
| 143 | 'traffic_disruption_threshold']: |
| 144 | asserts.fail('Test failed. Disruption Percentage = {}%. ' |
| 145 | 'Max traffic disruption: {}s.'.format( |
| 146 | disruption_percentage, max_disruption)) |
| 147 | else: |
| 148 | asserts.explicit_pass('Test failed. Disruption Percentage = {}%. ' |
| 149 | 'Max traffic disruption: {}s.'.format( |
| 150 | disruption_percentage, max_disruption)) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 151 | |
| 152 | def pass_fail_roaming_consistency(self, results_dict): |
| 153 | """Function to evaluate roaming consistency results. |
| 154 | |
| 155 | The function looks for the roams recorded in multiple runs of the same |
| 156 | attenuation waveform and checks that the DUT reliably roams to the |
| 157 | same network |
| 158 | |
| 159 | Args: |
| 160 | results_dict: dict containing consistency test results |
| 161 | """ |
| 162 | test_fail = False |
| 163 | for secondary_atten, roam_stats in results_dict['roam_stats'].items(): |
| 164 | total_roams = sum(list(roam_stats.values())) |
| 165 | common_roam = max(roam_stats.keys(), key=(lambda k: roam_stats[k])) |
| 166 | common_roam_frequency = roam_stats[common_roam] / total_roams |
| 167 | self.log.info( |
| 168 | '{}dB secondary atten. Most common roam: {}. Frequency: {}'. |
| 169 | format(secondary_atten, common_roam, common_roam_frequency)) |
| 170 | if common_roam_frequency < self.testclass_params[ |
| 171 | 'consistency_threshold']: |
| 172 | test_fail = True |
| 173 | self.log.info('Unstable Roams at {}dB secondary att'.format( |
| 174 | secondary_atten)) |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 175 | self.testcase_metric_logger.add_metric('common_roam_frequency', |
| 176 | common_roam_frequency) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 177 | if test_fail: |
| 178 | asserts.fail('Incosistent roaming detected.') |
| 179 | else: |
| 180 | asserts.explicit_pass('Consistent roaming at all levels.') |
| 181 | |
| 182 | def process_traffic_continuity_results(self, testcase_params, result): |
| 183 | """Function to process traffic results. |
| 184 | |
| 185 | The function looks for traffic gaps during a roaming test |
| 186 | |
| 187 | Args: |
| 188 | testcase_params: dict containing all test results and meta data |
| 189 | results_dict: dict containing consistency test results |
| 190 | """ |
| 191 | self.detect_roam_events(result) |
Omar El Ayach | 68395c4 | 2019-03-01 11:25:47 -0800 | [diff] [blame] | 192 | current_context = context.get_current_context().get_full_output_path() |
| 193 | plot_file_path = os.path.join(current_context, |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 194 | self.current_test_name + '.html') |
| 195 | |
| 196 | if 'ping' in self.current_test_name: |
| 197 | self.detect_ping_gaps(result) |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 198 | self.plot_ping_result(testcase_params, |
| 199 | result, |
| 200 | output_file_path=plot_file_path) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 201 | elif 'iperf' in self.current_test_name: |
| 202 | self.detect_iperf_gaps(result) |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 203 | self.plot_iperf_result(testcase_params, |
| 204 | result, |
| 205 | output_file_path=plot_file_path) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 206 | |
Omar El Ayach | 68395c4 | 2019-03-01 11:25:47 -0800 | [diff] [blame] | 207 | results_file_path = os.path.join(current_context, |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 208 | self.current_test_name + '.json') |
| 209 | with open(results_file_path, 'w') as results_file: |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 210 | json.dump(wputils.serialize_dict(result), results_file, indent=4) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 211 | |
| 212 | def process_consistency_results(self, testcase_params, results_dict): |
| 213 | """Function to process roaming consistency results. |
| 214 | |
| 215 | The function looks compiles the test of roams recorded in consistency |
| 216 | tests and plots results for easy visualization. |
| 217 | |
| 218 | Args: |
| 219 | testcase_params: dict containing all test results and meta data |
| 220 | results_dict: dict containing consistency test results |
| 221 | """ |
| 222 | # make figure placeholder and get relevant functions |
| 223 | if 'ping' in self.current_test_name: |
| 224 | detect_gaps = self.detect_ping_gaps |
| 225 | plot_result = self.plot_ping_result |
| 226 | primary_y_axis = 'RTT (ms)' |
| 227 | elif 'iperf' in self.current_test_name: |
| 228 | detect_gaps = self.detect_iperf_gaps |
| 229 | plot_result = self.plot_iperf_result |
| 230 | primary_y_axis = 'Throughput (Mbps)' |
| 231 | # loop over results |
| 232 | roam_stats = collections.OrderedDict() |
Omar El Ayach | 68395c4 | 2019-03-01 11:25:47 -0800 | [diff] [blame] | 233 | current_context = context.get_current_context().get_full_output_path() |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 234 | for secondary_atten, results_list in results_dict.items(): |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 235 | figure = wputils.BokehFigure(title=self.current_test_name, |
| 236 | x_label='Time (ms)', |
| 237 | primary_y_label=primary_y_axis, |
| 238 | secondary_y_label='RSSI (dBm)') |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 239 | roam_stats[secondary_atten] = collections.OrderedDict() |
| 240 | for result in results_list: |
| 241 | self.detect_roam_events(result) |
| 242 | for roam_transition, count in result['roam_counts'].items(): |
| 243 | roam_stats[secondary_atten][ |
| 244 | roam_transition] = roam_stats[secondary_atten].get( |
| 245 | roam_transition, 0) + count |
| 246 | detect_gaps(result) |
| 247 | plot_result(testcase_params, result, figure=figure) |
| 248 | # save plot |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 249 | plot_file_name = (self.current_test_name + '_' + |
| 250 | str(secondary_atten) + '.html') |
Omar El Ayach | 68395c4 | 2019-03-01 11:25:47 -0800 | [diff] [blame] | 251 | |
| 252 | plot_file_path = os.path.join(current_context, plot_file_name) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 253 | figure.save_figure(plot_file_path) |
| 254 | results_dict['roam_stats'] = roam_stats |
| 255 | |
Omar El Ayach | 68395c4 | 2019-03-01 11:25:47 -0800 | [diff] [blame] | 256 | results_file_path = os.path.join(current_context, |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 257 | self.current_test_name + '.json') |
| 258 | with open(results_file_path, 'w') as results_file: |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 259 | json.dump(wputils.serialize_dict(result), results_file, indent=4) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 260 | |
| 261 | def detect_roam_events(self, result): |
| 262 | """Function to process roaming results. |
| 263 | |
| 264 | The function detects roams by looking at changes in BSSID and compiles |
| 265 | meta data about each roam, e.g., RSSI before and after a roam. The |
| 266 | function then calls the relevant method to process traffic results and |
| 267 | report traffic disruptions. |
| 268 | |
| 269 | Args: |
| 270 | testcase_params: dict containing AP and other test params |
| 271 | result: dict containing test results |
| 272 | """ |
| 273 | roam_events = [ |
| 274 | (idx, idx + 1) |
| 275 | for idx in range(len(result['rssi_result']['bssid']) - 1) |
| 276 | if result['rssi_result']['bssid'][idx] != result['rssi_result'] |
| 277 | ['bssid'][idx + 1] |
| 278 | ] |
| 279 | |
| 280 | def ignore_entry(vals): |
| 281 | for val in vals: |
| 282 | if val in {0} or math.isnan(val): |
| 283 | return True |
| 284 | return False |
| 285 | |
| 286 | for roam_idx, roam_event in enumerate(roam_events): |
| 287 | # Find true roam start by scanning earlier samples for valid data |
| 288 | while ignore_entry([ |
| 289 | result['rssi_result']['frequency'][roam_event[0]], |
| 290 | result['rssi_result']['signal_poll_rssi']['data'][ |
| 291 | roam_event[0]] |
| 292 | ]): |
| 293 | roam_event = (roam_event[0] - 1, roam_event[1]) |
| 294 | roam_events[roam_idx] = roam_event |
| 295 | # Find true roam end by scanning later samples for valid data |
| 296 | while ignore_entry([ |
| 297 | result['rssi_result']['frequency'][roam_event[1]], |
| 298 | result['rssi_result']['signal_poll_rssi']['data'][ |
| 299 | roam_event[1]] |
| 300 | ]): |
| 301 | roam_event = (roam_event[0], roam_event[1] + 1) |
| 302 | roam_events[roam_idx] = roam_event |
| 303 | |
| 304 | roam_events = list(set(roam_events)) |
| 305 | roam_events.sort(key=lambda event_tuple: event_tuple[1]) |
| 306 | roam_transitions = [] |
| 307 | roam_counts = {} |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 308 | total_roams = 0 |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 309 | for event in roam_events: |
| 310 | from_bssid = next( |
| 311 | key for key, value in self.main_network.items() |
| 312 | if value['BSSID'] == result['rssi_result']['bssid'][event[0]]) |
| 313 | to_bssid = next( |
| 314 | key for key, value in self.main_network.items() |
| 315 | if value['BSSID'] == result['rssi_result']['bssid'][event[1]]) |
| 316 | curr_bssid_transition = (from_bssid, to_bssid) |
| 317 | curr_roam_transition = ( |
| 318 | (from_bssid, |
| 319 | result['rssi_result']['signal_poll_rssi']['data'][event[0]]), |
| 320 | (to_bssid, |
| 321 | result['rssi_result']['signal_poll_rssi']['data'][event[1]])) |
| 322 | roam_transitions.append(curr_roam_transition) |
| 323 | roam_counts[curr_bssid_transition] = roam_counts.get( |
| 324 | curr_bssid_transition, 0) + 1 |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 325 | total_roams = total_roams + 1 |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 326 | result['roam_events'] = roam_events |
| 327 | result['roam_transitions'] = roam_transitions |
| 328 | result['roam_counts'] = roam_counts |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 329 | result['total_roams'] = total_roams |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 330 | |
| 331 | def detect_ping_gaps(self, result): |
| 332 | """Function to process ping results. |
| 333 | |
| 334 | The function looks for gaps in iperf traffic and reports them as |
| 335 | disruptions due to roams. |
| 336 | |
| 337 | Args: |
| 338 | result: dict containing test results |
| 339 | """ |
| 340 | traffic_disruption = [ |
| 341 | x for x in result['ping_result']['ping_interarrivals'] |
| 342 | if x > TRAFFIC_GAP_THRESH |
| 343 | ] |
| 344 | result['traffic_disruption'] = traffic_disruption |
| 345 | |
| 346 | def detect_iperf_gaps(self, result): |
| 347 | """Function to process iperf results. |
| 348 | |
| 349 | The function looks for gaps in iperf traffic and reports them as |
| 350 | disruptions due to roams. |
| 351 | |
| 352 | Args: |
| 353 | result: dict containing test results |
| 354 | """ |
| 355 | tput_thresholding = [tput < 1 for tput in result['throughput']] |
| 356 | window_size = int(TRAFFIC_GAP_THRESH / IPERF_INTERVAL) |
| 357 | tput_thresholding = [ |
| 358 | any(tput_thresholding[max(0, idx - window_size):idx]) |
| 359 | for idx in range(1, |
| 360 | len(tput_thresholding) + 1) |
| 361 | ] |
| 362 | |
| 363 | traffic_disruption = [] |
| 364 | current_disruption = 1 - window_size |
| 365 | for tput_low in tput_thresholding: |
| 366 | if tput_low: |
| 367 | current_disruption += 1 |
| 368 | elif current_disruption > window_size: |
| 369 | traffic_disruption.append(current_disruption * IPERF_INTERVAL) |
| 370 | current_disruption = 1 - window_size |
| 371 | else: |
| 372 | current_disruption = 1 - window_size |
| 373 | result['traffic_disruption'] = traffic_disruption |
| 374 | |
| 375 | def plot_ping_result(self, |
| 376 | testcase_params, |
| 377 | result, |
| 378 | figure=None, |
| 379 | output_file_path=None): |
| 380 | """Function to plot ping results. |
| 381 | |
| 382 | The function plots ping RTTs along with RSSI over time during a roaming |
| 383 | test. |
| 384 | |
| 385 | Args: |
| 386 | testcase_params: dict containing all test params |
| 387 | result: dict containing test results |
| 388 | figure: optional bokeh figure object to add current plot to |
| 389 | output_file_path: optional path to output file |
| 390 | """ |
| 391 | if not figure: |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 392 | figure = wputils.BokehFigure(title=self.current_test_name, |
| 393 | x_label='Time (ms)', |
| 394 | primary_y_label='RTT (ms)', |
| 395 | secondary_y_label='RSSI (dBm)') |
| 396 | figure.add_line(x_data=result['ping_result']['time_stamp'], |
| 397 | y_data=result['ping_result']['rtt'], |
| 398 | legend='Ping RTT', |
| 399 | width=1) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 400 | figure.add_line( |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 401 | x_data=result['rssi_result']['time_stamp'], |
| 402 | y_data=result['rssi_result']['signal_poll_rssi']['data'], |
| 403 | legend='RSSI', |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 404 | y_axis='secondary') |
| 405 | figure.generate_figure(output_file_path) |
| 406 | |
| 407 | def plot_iperf_result(self, |
| 408 | testcase_params, |
| 409 | result, |
| 410 | figure=None, |
| 411 | output_file_path=None): |
| 412 | """Function to plot iperf results. |
| 413 | |
| 414 | The function plots iperf throughput and RSSI over time during a roaming |
| 415 | test. |
| 416 | |
| 417 | Args: |
| 418 | testcase_params: dict containing all test params |
| 419 | result: dict containing test results |
| 420 | figure: optional bokeh figure object to add current plot to |
| 421 | output_file_path: optional path to output file |
| 422 | """ |
| 423 | if not figure: |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 424 | figure = wputils.BokehFigure(title=self.current_test_name, |
| 425 | x_label='Time (s)', |
| 426 | primary_y_label='Throughput (Mbps)', |
| 427 | secondary_y_label='RSSI (dBm)') |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 428 | iperf_time_stamps = [ |
| 429 | idx * IPERF_INTERVAL for idx in range(len(result['throughput'])) |
| 430 | ] |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 431 | figure.add_line(iperf_time_stamps, |
| 432 | result['throughput'], |
| 433 | 'Throughput', |
| 434 | width=1) |
| 435 | figure.add_line(result['rssi_result']['time_stamp'], |
| 436 | result['rssi_result']['signal_poll_rssi']['data'], |
| 437 | 'RSSI', |
| 438 | y_axis='secondary') |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 439 | |
| 440 | figure.generate_figure(output_file_path) |
| 441 | |
| 442 | def setup_ap(self, testcase_params): |
| 443 | """Sets up the AP and attenuator to the test configuration. |
| 444 | |
| 445 | Args: |
| 446 | testcase_params: dict containing AP and other test params |
| 447 | """ |
| 448 | (primary_net_id, |
| 449 | primary_net_config) = next(net for net in self.main_network.items() |
| 450 | if net[1]['roaming_label'] == 'primary') |
Omar El Ayach | 98be238 | 2019-09-04 15:11:50 -0700 | [diff] [blame] | 451 | for idx, atten in enumerate(self.attenuators): |
| 452 | nets_on_port = [ |
| 453 | item["network"] for item in self.rf_map_by_atten[idx] |
| 454 | ] |
| 455 | if primary_net_id in nets_on_port: |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 456 | atten.set_atten(0) |
| 457 | else: |
| 458 | atten.set_atten(atten.instrument.max_atten) |
| 459 | |
| 460 | def setup_dut(self, testcase_params): |
| 461 | """Sets up the DUT in the configuration required by the test. |
| 462 | |
| 463 | Args: |
| 464 | testcase_params: dict containing AP and other test params |
| 465 | """ |
Omar El Ayach | 656bf3d | 2019-07-31 15:04:53 -0700 | [diff] [blame] | 466 | # Check battery level before test |
| 467 | if not wputils.health_check(self.dut, 10): |
| 468 | asserts.skip('Battery level too low. Skipping test.') |
Omar El Ayach | 74f78dc | 2019-07-30 15:15:34 -0700 | [diff] [blame] | 469 | wutils.reset_wifi(self.dut) |
Roshan Pius | 5b19a12 | 2019-09-13 08:07:30 -0700 | [diff] [blame] | 470 | wutils.set_wifi_country_code(self.dut, |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 471 | self.testclass_params['country_code']) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 472 | (primary_net_id, |
| 473 | primary_net_config) = next(net for net in self.main_network.items() |
| 474 | if net[1]['roaming_label'] == 'primary') |
| 475 | network = primary_net_config.copy() |
| 476 | network.pop('BSSID', None) |
Omar El Ayach | 74f78dc | 2019-07-30 15:15:34 -0700 | [diff] [blame] | 477 | self.dut.droid.wifiSetEnableAutoJoinWhenAssociated(1) |
Omar El Ayach | d30d398 | 2020-04-06 14:07:01 -0700 | [diff] [blame^] | 478 | wutils.wifi_connect(self.dut, |
| 479 | network, |
| 480 | num_of_tries=5, |
| 481 | check_connectivity=False) |
Omar El Ayach | 74f78dc | 2019-07-30 15:15:34 -0700 | [diff] [blame] | 482 | self.dut.droid.wifiSetEnableAutoJoinWhenAssociated(1) |
| 483 | self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0] |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 484 | if testcase_params['screen_on']: |
Omar El Ayach | 74f78dc | 2019-07-30 15:15:34 -0700 | [diff] [blame] | 485 | self.dut.wakeup_screen() |
| 486 | self.dut.droid.wakeLockAcquireBright() |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 487 | time.sleep(MED_SLEEP) |
| 488 | |
| 489 | def setup_roaming_test(self, testcase_params): |
| 490 | """Function to set up roaming test.""" |
| 491 | self.setup_ap(testcase_params) |
| 492 | self.setup_dut(testcase_params) |
| 493 | |
| 494 | def run_ping_test(self, testcase_params): |
| 495 | """Main function for ping roaming tests. |
| 496 | |
| 497 | Args: |
| 498 | testcase_params: dict including all test params encoded in test |
| 499 | name |
| 500 | Returns: |
| 501 | dict containing all test results and meta data |
| 502 | """ |
| 503 | self.log.info('Starting ping test.') |
| 504 | ping_future = wputils.get_ping_stats_nb( |
| 505 | self.remote_server, self.dut_ip, |
| 506 | testcase_params['atten_waveforms']['length'], |
| 507 | testcase_params['ping_interval'], 64) |
| 508 | rssi_future = wputils.get_connected_rssi_nb( |
Omar El Ayach | 74f78dc | 2019-07-30 15:15:34 -0700 | [diff] [blame] | 509 | self.dut, |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 510 | int(testcase_params['atten_waveforms']['length'] / |
| 511 | testcase_params['rssi_polling_frequency']), |
| 512 | testcase_params['rssi_polling_frequency']) |
| 513 | self.run_attenuation_waveform(testcase_params) |
| 514 | return { |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 515 | 'ping_result': ping_future.result().as_dict(), |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 516 | 'rssi_result': rssi_future.result(), |
| 517 | 'ap_settings': self.access_point.ap_settings, |
| 518 | } |
| 519 | |
| 520 | def run_iperf_test(self, testcase_params): |
| 521 | """Main function for iperf roaming tests. |
| 522 | |
| 523 | Args: |
| 524 | testcase_params: dict including all test params encoded in test |
| 525 | name |
| 526 | Returns: |
| 527 | result: dict containing all test results and meta data |
| 528 | """ |
| 529 | self.log.info('Starting iperf test.') |
| 530 | self.iperf_server.start(extra_args='-i {}'.format(IPERF_INTERVAL)) |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 531 | self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0] |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 532 | if isinstance(self.iperf_server, ipf.IPerfServerOverAdb): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 533 | iperf_server_address = self.dut_ip |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 534 | else: |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 535 | iperf_server_address = wputils.get_server_address( |
| 536 | self.remote_server, self.dut_ip, '255.255.255.0') |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 537 | iperf_args = '-i {} -t {} -J'.format( |
| 538 | IPERF_INTERVAL, testcase_params['atten_waveforms']['length']) |
| 539 | if not isinstance(self.iperf_server, ipf.IPerfServerOverAdb): |
| 540 | iperf_args = iperf_args + ' -R' |
| 541 | iperf_future = wputils.start_iperf_client_nb( |
| 542 | self.iperf_client, iperf_server_address, iperf_args, 0, |
| 543 | testcase_params['atten_waveforms']['length'] + MED_SLEEP) |
| 544 | rssi_future = wputils.get_connected_rssi_nb( |
Omar El Ayach | 74f78dc | 2019-07-30 15:15:34 -0700 | [diff] [blame] | 545 | self.dut, |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 546 | int(testcase_params['atten_waveforms']['length'] / |
| 547 | testcase_params['rssi_polling_frequency']), |
| 548 | testcase_params['rssi_polling_frequency']) |
| 549 | self.run_attenuation_waveform(testcase_params) |
| 550 | client_output_path = iperf_future.result() |
| 551 | server_output_path = self.iperf_server.stop() |
| 552 | if isinstance(self.iperf_server, ipf.IPerfServerOverAdb): |
| 553 | iperf_file = server_output_path |
| 554 | else: |
| 555 | iperf_file = client_output_path |
| 556 | iperf_result = ipf.IPerfResult(iperf_file) |
Omar El Ayach | 98be238 | 2019-09-04 15:11:50 -0700 | [diff] [blame] | 557 | instantaneous_rates = [ |
| 558 | rate * 8 * (1.024**2) for rate in iperf_result.instantaneous_rates |
| 559 | ] |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 560 | return { |
Omar El Ayach | 98be238 | 2019-09-04 15:11:50 -0700 | [diff] [blame] | 561 | 'throughput': instantaneous_rates, |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 562 | 'rssi_result': rssi_future.result(), |
| 563 | 'ap_settings': self.access_point.ap_settings, |
| 564 | } |
| 565 | |
| 566 | def run_attenuation_waveform(self, testcase_params, step_duration=1): |
| 567 | """Function that generates test params based on the test name. |
| 568 | |
| 569 | Args: |
| 570 | testcase_params: dict including all test params encoded in test |
| 571 | name |
| 572 | step_duration: int representing number of seconds to dwell on each |
| 573 | atten level |
| 574 | """ |
| 575 | atten_waveforms = testcase_params['atten_waveforms'] |
| 576 | for atten_idx in range(atten_waveforms['length']): |
| 577 | start_time = time.time() |
| 578 | for network, atten_waveform in atten_waveforms.items(): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 579 | for idx, atten in enumerate(self.attenuators): |
| 580 | nets_on_port = [ |
| 581 | item["network"] for item in self.rf_map_by_atten[idx] |
| 582 | ] |
| 583 | if network in nets_on_port: |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 584 | atten.set_atten(atten_waveform[atten_idx]) |
| 585 | measure_time = time.time() - start_time |
| 586 | time.sleep(step_duration - measure_time) |
| 587 | |
| 588 | def compile_atten_waveforms(self, waveform_params): |
| 589 | """Function to compile all attenuation waveforms for roaming test. |
| 590 | |
| 591 | Args: |
| 592 | waveform_params: list of dicts representing waveforms to generate |
| 593 | """ |
| 594 | atten_waveforms = {} |
| 595 | for network in list(waveform_params[0]): |
| 596 | atten_waveforms[network] = [] |
| 597 | |
| 598 | for waveform in waveform_params: |
| 599 | for network, network_waveform in waveform.items(): |
| 600 | waveform_vector = self.gen_single_atten_waveform( |
| 601 | network_waveform) |
| 602 | atten_waveforms[network] += waveform_vector |
| 603 | |
| 604 | waveform_lengths = { |
| 605 | len(atten_waveforms[network]) |
| 606 | for network in atten_waveforms.keys() |
| 607 | } |
| 608 | if len(waveform_lengths) != 1: |
| 609 | raise ValueError( |
| 610 | 'Attenuation waveform length should be equal for all networks.' |
| 611 | ) |
| 612 | else: |
| 613 | atten_waveforms['length'] = waveform_lengths.pop() |
| 614 | return atten_waveforms |
| 615 | |
| 616 | def gen_single_atten_waveform(self, waveform_params): |
| 617 | """Function to generate a single attenuation waveform for roaming test. |
| 618 | |
| 619 | Args: |
| 620 | waveform_params: dict representing waveform to generate |
| 621 | """ |
| 622 | waveform_vector = [] |
| 623 | for section in range(len(waveform_params['atten_levels']) - 1): |
| 624 | section_limits = waveform_params['atten_levels'][section:section + |
| 625 | 2] |
| 626 | up_down = (1 - 2 * (section_limits[1] < section_limits[0])) |
| 627 | temp_section = list( |
| 628 | range(section_limits[0], section_limits[1] + up_down, |
| 629 | up_down * waveform_params['step_size'])) |
| 630 | temp_section = [ |
| 631 | val for val in temp_section |
| 632 | for _ in range(waveform_params['step_duration']) |
| 633 | ] |
| 634 | waveform_vector += temp_section |
| 635 | waveform_vector *= waveform_params['repetitions'] |
| 636 | return waveform_vector |
| 637 | |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 638 | def parse_test_params(self, testcase_params): |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 639 | """Function that generates test params based on the test name. |
| 640 | |
| 641 | Args: |
| 642 | test_name: current test name |
| 643 | Returns: |
| 644 | testcase_params: dict including all test params encoded in test |
| 645 | name |
| 646 | """ |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 647 | if testcase_params["waveform_type"] == 'smooth': |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 648 | testcase_params[ |
| 649 | 'roaming_waveforms_params'] = self.testclass_params[ |
| 650 | 'smooth_roaming_waveforms'] |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 651 | elif testcase_params["waveform_type"] == 'failover': |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 652 | testcase_params[ |
| 653 | 'roaming_waveforms_params'] = self.testclass_params[ |
| 654 | 'failover_roaming_waveforms'] |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 655 | elif testcase_params["waveform_type"] == 'consistency': |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 656 | testcase_params[ |
| 657 | 'roaming_waveforms_params'] = self.testclass_params[ |
| 658 | 'consistency_waveforms'] |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 659 | return testcase_params |
| 660 | |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 661 | def _test_traffic_continuity(self, testcase_params): |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 662 | """Test function for traffic continuity""" |
| 663 | # Compile test parameters from config and test name |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 664 | testcase_params = self.parse_test_params(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 665 | testcase_params.update(self.testclass_params) |
| 666 | testcase_params['atten_waveforms'] = self.compile_atten_waveforms( |
| 667 | testcase_params['roaming_waveforms_params']) |
| 668 | # Run traffic test |
| 669 | self.setup_roaming_test(testcase_params) |
| 670 | if testcase_params['traffic_type'] == 'iperf': |
| 671 | result = self.run_iperf_test(testcase_params) |
| 672 | elif testcase_params['traffic_type'] == 'ping': |
| 673 | result = self.run_ping_test(testcase_params) |
| 674 | # Postprocess results |
| 675 | self.process_traffic_continuity_results(testcase_params, result) |
| 676 | self.pass_fail_traffic_continuity(result) |
| 677 | |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 678 | def _test_roam_consistency(self, testcase_params): |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 679 | """Test function for roaming consistency""" |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 680 | testcase_params = self.parse_test_params(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 681 | testcase_params.update(self.testclass_params) |
| 682 | # Run traffic test |
| 683 | secondary_attens = range( |
| 684 | self.testclass_params['consistency_waveforms']['secondary_loop'] |
| 685 | ['atten_levels'][0], self.testclass_params['consistency_waveforms'] |
| 686 | ['secondary_loop']['atten_levels'][1], |
| 687 | self.testclass_params['consistency_waveforms']['secondary_loop'] |
| 688 | ['step_size']) |
| 689 | results = collections.OrderedDict() |
| 690 | for secondary_atten in secondary_attens: |
| 691 | primary_waveform = self.gen_single_atten_waveform( |
| 692 | testcase_params['roaming_waveforms_params']['primary_sweep']) |
| 693 | secondary_waveform_params = { |
| 694 | 'atten_levels': [secondary_atten, secondary_atten], |
| 695 | 'step_size': 1, |
| 696 | 'step_duration': len(primary_waveform), |
| 697 | 'repetitions': 1 |
| 698 | } |
| 699 | secondary_waveform = self.gen_single_atten_waveform( |
| 700 | secondary_waveform_params) |
| 701 | testcase_params['atten_waveforms'] = { |
| 702 | 'length': len(primary_waveform) |
| 703 | } |
| 704 | for network_key, network_info in self.main_network.items(): |
| 705 | if 'primary' in network_info['roaming_label']: |
| 706 | testcase_params['atten_waveforms'][ |
| 707 | network_key] = primary_waveform |
| 708 | else: |
| 709 | testcase_params['atten_waveforms'][ |
| 710 | network_key] = secondary_waveform |
| 711 | results[secondary_atten] = [] |
| 712 | for run in range(self.testclass_params['consistency_num_runs']): |
| 713 | self.setup_roaming_test(testcase_params) |
| 714 | results[secondary_atten].append( |
| 715 | self.run_ping_test(testcase_params)) |
| 716 | # Postprocess results |
| 717 | self.process_consistency_results(testcase_params, results) |
| 718 | self.pass_fail_roaming_consistency(results) |
| 719 | |
| 720 | def test_consistency_roaming_screen_on_ping(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 721 | testcase_params = { |
| 722 | "waveform_type": "consistency", |
| 723 | "screen_on": 1, |
| 724 | "traffic_type": "ping" |
| 725 | } |
| 726 | self._test_roam_consistency(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 727 | |
| 728 | def test_smooth_roaming_screen_on_ping_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 729 | testcase_params = { |
| 730 | "waveform_type": "smooth", |
| 731 | "screen_on": 1, |
| 732 | "traffic_type": "ping" |
| 733 | } |
| 734 | self._test_traffic_continuity(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 735 | |
| 736 | def test_smooth_roaming_screen_on_iperf_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 737 | testcase_params = { |
| 738 | "waveform_type": "smooth", |
| 739 | "screen_on": 1, |
| 740 | "traffic_type": "iperf" |
| 741 | } |
| 742 | self._test_traffic_continuity(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 743 | |
| 744 | def test_failover_roaming_screen_on_ping_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 745 | testcase_params = { |
| 746 | "waveform_type": "failover", |
| 747 | "screen_on": 1, |
| 748 | "traffic_type": "ping" |
| 749 | } |
| 750 | self._test_traffic_continuity(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 751 | |
| 752 | def test_failover_roaming_screen_on_iperf_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 753 | testcase_params = { |
| 754 | "waveform_type": "failover", |
| 755 | "screen_on": 1, |
| 756 | "traffic_type": "iperf" |
| 757 | } |
| 758 | self._test_traffic_continuity(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 759 | |
| 760 | def test_smooth_roaming_screen_off_ping_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 761 | testcase_params = { |
| 762 | "waveform_type": "smooth", |
| 763 | "screen_on": 0, |
| 764 | "traffic_type": "ping" |
| 765 | } |
| 766 | self._test_traffic_continuity(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 767 | |
| 768 | def test_smooth_roaming_screen_off_iperf_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 769 | testcase_params = { |
| 770 | "waveform_type": "smooth", |
| 771 | "screen_on": 0, |
| 772 | "traffic_type": "iperf" |
| 773 | } |
| 774 | self._test_traffic_continuity(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 775 | |
| 776 | def test_failover_roaming_screen_off_ping_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 777 | testcase_params = { |
| 778 | "waveform_type": "failover", |
| 779 | "screen_on": 0, |
| 780 | "traffic_type": "ping" |
| 781 | } |
| 782 | self._test_traffic_continuity(testcase_params) |
Omar El Ayach | b503ae8 | 2019-02-13 09:36:36 -0800 | [diff] [blame] | 783 | |
| 784 | def test_failover_roaming_screen_off_iperf_continuity(self): |
Omar El Ayach | 7347770 | 2019-08-07 10:12:33 -0700 | [diff] [blame] | 785 | testcase_params = { |
| 786 | "waveform_type": "failover", |
| 787 | "screen_on": 0, |
| 788 | "traffic_type": "iperf" |
| 789 | } |
| 790 | self._test_traffic_continuity(testcase_params) |