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 17import collections 18import itertools 19import json 20import os 21from acts import base_test 22from acts import context 23from acts.metrics.loggers.blackbox import BlackboxMetricLogger 24from acts.test_utils.wifi import ota_chamber 25from acts.test_utils.wifi import wifi_performance_test_utils as wputils 26from WifiRvrTest import WifiRvrTest 27from WifiPingTest import WifiPingTest 28 29 30class WifiOtaRvrTest(WifiRvrTest): 31 """Class to test over-the-air RvR 32 33 This class implements measures WiFi RvR tests in an OTA chamber. It enables 34 setting turntable orientation and other chamber parameters to study 35 performance in varying channel conditions 36 """ 37 38 def __init__(self, controllers): 39 base_test.BaseTestClass.__init__(self, controllers) 40 self.failure_count_metric = BlackboxMetricLogger.for_test_case( 41 metric_name='failure_count') 42 43 def setup_class(self): 44 WifiRvrTest.setup_class(self) 45 req_params = ['OTAChamber'] 46 self.unpack_userparams(req_params) 47 self.ota_chambers = ota_chamber.create(self.OTAChamber) 48 self.ota_chamber = self.ota_chambers[0] 49 50 def teardown_class(self): 51 WifiRvrTest.teardown_class(self) 52 self.ota_chamber.set_orientation(0) 53 54 def setup_rvr_test(self, testcase_params): 55 """Function that gets devices ready for the test. 56 57 Args: 58 testcase_params: dict containing test-specific parameters 59 """ 60 # Set turntable orientation 61 self.ota_chamber.set_orientation(testcase_params['orientation']) 62 # Continue test setup 63 WifiRvrTest.setup_rvr_test(self, testcase_params) 64 65 def parse_test_params(self, test_name): 66 """Function that generates test params based on the test name.""" 67 # Call parent parsing function 68 testcase_params = WifiRvrTest.parse_test_params(self, test_name) 69 # Add orientation information 70 test_name_params = test_name.split('_') 71 testcase_params['orientation'] = int(test_name_params[6][0:-3]) 72 return testcase_params 73 74 def generate_test_cases(self, channels, modes, angles, traffic_types, 75 directions): 76 test_cases = [] 77 testcase_wrapper = self._test_rvr 78 allowed_configs = { 79 'VHT20': [ 80 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153, 81 157, 161 82 ], 83 'VHT40': [36, 44, 149, 157], 84 'VHT80': [36, 149] 85 } 86 for channel, mode, angle, traffic_type, direction in itertools.product( 87 channels, modes, angles, traffic_types, directions): 88 if channel not in allowed_configs[mode]: 89 continue 90 testcase_name = 'test_rvr_{}_{}_ch{}_{}_{}deg'.format( 91 traffic_type, direction, channel, mode, angle) 92 setattr(self, testcase_name, testcase_wrapper) 93 test_cases.append(testcase_name) 94 return test_cases 95 96 97class WifiOtaRvr_StandardOrientation_Test(WifiOtaRvrTest): 98 def __init__(self, controllers): 99 WifiOtaRvrTest.__init__(self, controllers) 100 self.tests = self.generate_test_cases( 101 [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161], 102 ['VHT20', 'VHT40', 'VHT80'], list(range(0, 360, 103 45)), ['TCP'], ['DL']) 104 105 106class WifiOtaRvr_SampleChannel_Test(WifiOtaRvrTest): 107 def __init__(self, controllers): 108 WifiOtaRvrTest.__init__(self, controllers) 109 self.tests = self.generate_test_cases([6, 36, 149], ['VHT20', 'VHT80'], 110 list(range(0, 360, 45)), ['TCP'], 111 ['DL']) 112 113 114class WifiOtaRvr_SingleOrientation_Test(WifiOtaRvrTest): 115 def __init__(self, controllers): 116 WifiOtaRvrTest.__init__(self, controllers) 117 self.tests = self.generate_test_cases( 118 [6, 36, 40, 44, 48, 149, 153, 157, 161], 119 ['VHT20', 'VHT40', 'VHT80'], [0], ['TCP'], ['DL', 'UL']) 120 121 122# Ping Tests 123class WifiOtaPingTest(WifiPingTest): 124 """Class to test over-the-air ping 125 126 This class tests WiFi ping performance in an OTA chamber. It enables 127 setting turntable orientation and other chamber parameters to study 128 performance in varying channel conditions 129 """ 130 131 def __init__(self, controllers): 132 base_test.BaseTestClass.__init__(self, controllers) 133 self.ping_range_metric = BlackboxMetricLogger.for_test_case( 134 metric_name='ping_range') 135 self.ping_rtt_metric = BlackboxMetricLogger.for_test_case( 136 metric_name='ping_rtt') 137 138 def setup_class(self): 139 WifiPingTest.setup_class(self) 140 req_params = ['OTAChamber'] 141 self.unpack_userparams(req_params) 142 self.ota_chambers = ota_chamber.create(self.OTAChamber) 143 self.ota_chamber = self.ota_chambers[0] 144 145 def teardown_class(self): 146 self.process_testclass_results() 147 self.ota_chamber.set_orientation(0) 148 149 def process_testclass_results(self): 150 """Saves all test results to enable comparison.""" 151 WifiPingTest.process_testclass_results(self) 152 153 range_vs_angle = collections.OrderedDict() 154 for test in self.testclass_results: 155 curr_params = self.parse_test_params(test['test_name']) 156 curr_config = curr_params['channel'] 157 if curr_config in range_vs_angle: 158 range_vs_angle[curr_config]['orientation'].append( 159 curr_params['orientation']) 160 range_vs_angle[curr_config]['range'].append(test['range']) 161 else: 162 range_vs_angle[curr_config] = { 163 'orientation': [curr_params['orientation']], 164 'range': [test['range']] 165 } 166 figure = wputils.BokehFigure( 167 title='Range vs. Orientation', 168 x_label='Angle (deg)', 169 primary_y='Range (dB)', 170 ) 171 for config, config_data in range_vs_angle.items(): 172 figure.add_line(config_data['orientation'], config_data['range'], 173 'Channel {}'.format(config)) 174 current_context = context.get_current_context().get_full_output_path() 175 plot_file_path = os.path.join(current_context, 'results.html') 176 figure.generate_figure(plot_file_path) 177 178 # Save results 179 results_file_path = os.path.join(current_context, 180 'testclass_summary.json') 181 with open(results_file_path, 'w') as results_file: 182 json.dump(range_vs_angle, results_file, indent=4) 183 184 def setup_ping_test(self, testcase_params): 185 """Function that gets devices ready for the test. 186 187 Args: 188 testcase_params: dict containing test-specific parameters 189 """ 190 # Configure AP 191 self.setup_ap(testcase_params) 192 # Set attenuator to 0 dB 193 for attenuator in self.attenuators: 194 attenuator.set_atten(0, strict=False) 195 # Setup turntable 196 self.ota_chamber.set_orientation(testcase_params['orientation']) 197 # Reset, configure, and connect DUT 198 self.setup_dut(testcase_params) 199 200 def parse_test_params(self, test_name): 201 """Function that generates test params based on the test name.""" 202 # Call parent parsing function 203 testcase_params = WifiPingTest.parse_test_params(self, test_name) 204 # Add orientation information 205 test_name_params = test_name.split('_') 206 testcase_params['orientation'] = int(test_name_params[5][0:-3]) 207 return testcase_params 208 209 def generate_test_cases(self, channels, modes, angles): 210 test_cases = [] 211 testcase_wrapper = self._test_ping_range 212 allowed_configs = { 213 'VHT20': [ 214 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153, 215 157, 161 216 ], 217 'VHT40': [36, 44, 149, 157], 218 'VHT80': [36, 149] 219 } 220 for channel, mode, angle in itertools.product(channels, modes, angles): 221 if channel not in allowed_configs[mode]: 222 continue 223 testcase_name = 'test_ping_range_ch{}_{}_{}deg'.format( 224 channel, mode, angle) 225 setattr(self, testcase_name, testcase_wrapper) 226 test_cases.append(testcase_name) 227 return test_cases 228 229 230class WifiOtaPing_TenDegree_Test(WifiOtaPingTest): 231 def __init__(self, controllers): 232 WifiOtaPingTest.__init__(self, controllers) 233 self.tests = self.generate_test_cases( 234 [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161], ['VHT20'], 235 list(range(0, 360, 10))) 236 237 238class WifiOtaPing_45Degree_Test(WifiOtaPingTest): 239 def __init__(self, controllers): 240 WifiOtaPingTest.__init__(self, controllers) 241 self.tests = self.generate_test_cases( 242 [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161], ['VHT20'], 243 list(range(0, 360, 45))) 244