1# Copyright 2022 - The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the 'License'); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an 'AS IS' BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14import os 15import time 16 17from acts import asserts 18import acts_contrib.test_utils.power.cellular.cellular_power_preset_base_test as PB 19 20class PowerTelTrafficPresetTest(PB.PowerCellularPresetLabBaseTest): 21 # command to enable mobile data 22 ADB_CMD_ENABLE_MOBILE_DATA = 'svc data enable' 23 24 # command to start iperf server on UE 25 START_IPERF_SV_UE_CMD = 'nohup > /dev/null 2>&1 sh -c "iperf3 -s -i1 -p5201 > /dev/null &"' 26 27 # command to start iperf server on UE 28 # (require: 1.path to iperf exe 2.hostname/hostIP) 29 START_IPERF_CLIENT_UE_CMD = 'nohup > /dev/null 2>&1 sh -c "iperf3 -c {iperf_host_ip} -i1 -p5202 -w8m -t2000 -O{second} > /dev/null &"' 30 31 # command to start iperf server on host() 32 START_IPERF_SV_HOST_CMD = '{exe_path}\\iperf3 -s -p5202' 33 34 # command to start iperf client on host 35 # (require: 1.path to iperf exe 2.UE IP) 36 START_IPERF_CLIENT_HOST_CMD = ( 37 '{exe_path}\\iperf3 -c {ue_ip} -w16M -t1000 -p5201 -O{second}') 38 39 START_IPERF_CLIENT_HOST_CMD_FR2 = ( 40 '{exe_path}\\iperf3 -c {ue_ip} -w16M -t1000 -p5201 -O{second}') 41 42 def __init__(self, controllers): 43 super().__init__(controllers) 44 self.ssh_iperf_client = None 45 self.ssh_iperf_server = None 46 self.iperf_out_err = {} 47 48 def setup_class(self): 49 super().setup_class() 50 51 # Unpack test parameters used in this class 52 self.unpack_userparams(iperf_exe_path=None, 53 ue_ip=None, 54 iperf_host_ip=None) 55 56 # Verify required config 57 for param in ('iperf_exe_path', 'ue_ip', 'iperf_host_ip'): 58 if getattr(self, param) is None: 59 raise RuntimeError( 60 f'Parameter "{param}" is required to run this type of test') 61 62 def setup_test(self): 63 # Call parent method first to setup simulation 64 super().setup_test() 65 66 # get tput configs 67 self.unpack_userparams(abnormal_bandwidth_tolerance=0.1, 68 bandwidth_tolerance=0.1, 69 n_second_to_omitted=45) 70 self.expected_downlink_bandwidth = float(self.test_configs[self.test_name]['tput']['downlink'].split()[0]) 71 self.expected_uplink_bandwidth = float(self.test_configs[self.test_name]['tput']['uplink'].split()[0]) 72 73 # setup ssh client 74 self.ssh_iperf_client = self.cellular_simulator.create_ssh_client() 75 self.ssh_iperf_server = self.cellular_simulator.create_ssh_client() 76 77 self.turn_on_mobile_data() 78 79 def power_tel_traffic_test(self): 80 """Measure power while data is transferring.""" 81 # Start data traffic 82 self.start_uplink_process() 83 self.start_downlink_process() 84 85 # Measure power and check against threshold 86 self.collect_power_data_and_validate() 87 88 def _end_iperfs(self): 89 err_message = [] 90 # Write iperf log 91 self.ssh_iperf_server.close() 92 uplink_log_name = self.test_name + '_uplink.txt' 93 out, err = self.iperf_out_err[self.ssh_iperf_server] 94 output_content = ''.join(out.readlines()) 95 err_content = ''.join(err.readlines()) 96 self._write_iperf_log(uplink_log_name, output_content + err_content) 97 if err_content.strip(): 98 err_message.append(f'Uplink process fail due to error: {err_content}\n') 99 else: 100 if not self._iperf_log_check(output_content, self.expected_uplink_bandwidth): 101 err_message.append('Bandwidth of uplink process is unstable.') 102 103 self.ssh_iperf_client.close() 104 downlink_log_name = self.test_name + '_downlink.txt' 105 out, err = self.iperf_out_err[self.ssh_iperf_client] 106 output_content = ''.join(out.readlines()) 107 err_content = ''.join(err.readlines()) 108 self._write_iperf_log(downlink_log_name, output_content + err_content) 109 if err_content.strip(): 110 err_message.append(f'Downlink process fail due to error: {err_content}\n') 111 else: 112 if not self._iperf_log_check(output_content, self.expected_downlink_bandwidth): 113 err_message.append('Bandwidth of downlink process is unstable.') 114 115 if err_message: 116 raise RuntimeError('\n'.join(err_message)) 117 118 def teardown_test(self): 119 try: 120 self._end_iperfs() 121 except RuntimeError as re: 122 raise re 123 finally: 124 super().teardown_test() 125 126 def _iperf_log_check(self, file, expected_bandwidth): 127 """Check iperf log and abnormal bandwidth instances. 128 129 Args: 130 file: file object of iperf log to be checked. 131 expected_bandwidth: integer value for expected bandwidth. 132 Returns: 133 True if log is normal, False otherwise. 134 """ 135 # example of record line 136 #[ 4] 0.00-1.00 sec 20.2 MBytes 169 Mbits/sec 137 total_abnormal_entries = 0 138 total_record_entries = 0 139 bandwidth_val_idx = 6 140 record_entry_total_cols = 8 141 lines = file.split('\n') 142 acceptable_difference = self.bandwidth_tolerance * expected_bandwidth 143 self.log.debug('Expected bandwidth: %f', expected_bandwidth) 144 self.log.debug('Acceptance difference: %f', acceptable_difference) 145 for line in lines: 146 cols = line.split() 147 self.log.debug(cols) 148 if len(cols) == record_entry_total_cols: 149 total_record_entries += 1 150 bandwidth = float(cols[bandwidth_val_idx]) 151 self.log.debug('bandwidth: %f', bandwidth) 152 if abs(bandwidth - expected_bandwidth) > acceptable_difference: 153 total_abnormal_entries += 1 154 if not total_record_entries: 155 raise RuntimeError('No tput data record found.') 156 self.log.debug('Total abnormal entries: %d - Total record: %d', total_abnormal_entries, total_record_entries) 157 return (total_abnormal_entries/total_record_entries) <= self.abnormal_bandwidth_tolerance 158 159 def _exec_ssh_cmd(self, ssh_client, cmd): 160 """Execute command on given ssh client. 161 162 Args: 163 ssh_client: parmiko ssh client object. 164 cmd: command to execute via ssh. 165 """ 166 self.log.info('Sending cmd to ssh host: ' + cmd) 167 stdin, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True) 168 stdin.close() 169 self.iperf_out_err[ssh_client] = (stdout, stderr) 170 171 def start_downlink_process(self): 172 """UE transfer data to host.""" 173 self.log.info('Start downlink process') 174 # start UE iperf server 175 self.cellular_dut.ad.adb.shell(self.START_IPERF_SV_UE_CMD) 176 self.log.info('cmd sent to UE: ' + self.START_IPERF_SV_UE_CMD) 177 self.log.info('UE iperf server started') 178 time.sleep(5) 179 # start host iperf client 180 cmd = None 181 if 'fr2' in self.test_name: 182 cmd = self.START_IPERF_CLIENT_HOST_CMD_FR2.format( 183 exe_path=self.iperf_exe_path, 184 ue_ip=self.ue_ip, 185 second=self.n_second_to_omitted) 186 else: 187 cmd = self.START_IPERF_CLIENT_HOST_CMD.format( 188 exe_path=self.iperf_exe_path, 189 ue_ip=self.ue_ip, 190 second=self.n_second_to_omitted) 191 192 if not cmd: 193 raise RuntimeError('Cannot format command to start iperf client.') 194 self._exec_ssh_cmd(self.ssh_iperf_client, cmd) 195 self.log.info('Host iperf client started') 196 time.sleep(5) 197 198 def start_uplink_process(self): 199 """Host transfer data to UE.""" 200 self.log.info('Start uplink process') 201 # start host iperf server 202 cmd = self.START_IPERF_SV_HOST_CMD.format(exe_path=self.iperf_exe_path) 203 self._exec_ssh_cmd(self.ssh_iperf_server, cmd) 204 self.log.info('Host iperf server started') 205 time.sleep(5) 206 # start UE iperf 207 adb_cmd = self.START_IPERF_CLIENT_UE_CMD.format( 208 iperf_host_ip=self.iperf_host_ip, 209 second=self.n_second_to_omitted) 210 self.cellular_dut.ad.adb.shell(adb_cmd) 211 self.log.info('cmd sent to UE: ' + adb_cmd) 212 self.log.info('UE iperf client started') 213 time.sleep(5) 214 215 def _write_iperf_log(self, file_name, content): 216 """ Writing ssh stdout and stdin to log file. 217 218 Args: 219 file_name: Log file name to write log to. 220 content: Content to write to file. 221 """ 222 iperf_log_dir = os.path.join(self.root_output_path, 'iperf') 223 os.makedirs(iperf_log_dir, exist_ok=True) 224 iperf_log_file_path = os.path.join(iperf_log_dir, file_name) 225 with open(iperf_log_file_path, 'w') as f: 226 f.write(content) 227 228 def turn_on_mobile_data(self): 229 self.dut.adb.shell(self.ADB_CMD_ENABLE_MOBILE_DATA) 230 231 232class PowerTelTraffic_Preset_Test(PowerTelTrafficPresetTest): 233 def test_preset_LTE_traffic(self): 234 self.power_tel_traffic_test() 235 236 def test_preset_nsa_traffic_fr1(self): 237 self.power_tel_traffic_test() 238 239 def test_preset_sa_traffic_fr1(self): 240 self.power_tel_traffic_test() 241 242 243class PowerTelTrafficFr2_Preset_Test(PowerTelTrafficPresetTest): 244 def test_preset_nsa_traffic_fr2(self): 245 self.power_tel_traffic_test() 246