1#!/usr/bin/env python3 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 time 18 19from acts import base_test 20from acts import asserts 21from acts.controllers.rohdeschwarz_lib import contest 22from acts_contrib.test_utils.tel import tel_test_utils 23from acts.metrics.loggers import blackbox 24 25import json 26 27 28class AGNSSPerformanceTest(base_test.BaseTestClass): 29 30 # User parameters defined in the ACTS config file 31 32 TESTPLAN_KEY = '{}_testplan' 33 CONTEST_IP_KEY = 'contest_ip' 34 REMOTE_SERVER_PORT_KEY = 'remote_server_port' 35 AUTOMATION_PORT_KEY = 'automation_port' 36 CUSTOM_FILES_KEY = 'custom_files' 37 AUTOMATION_LISTEN_IP = 'automation_listen_ip' 38 FTP_USER_KEY = 'ftp_user' 39 FTP_PASSWORD_KEY = 'ftp_password' 40 41 def __init__(self, controllers): 42 """ Initializes class attributes. """ 43 44 super().__init__(controllers) 45 46 self.dut = None 47 self.contest = None 48 self.testplan = None 49 self.thresholds_file = None 50 51 self.ttf_metric = blackbox.BlackboxMetricLogger.for_test_case( 52 metric_name='ttf') 53 self.pos_error_metric = blackbox.BlackboxMetricLogger.for_test_case( 54 metric_name='pos_error') 55 self.sensitivity_metric = blackbox.BlackboxMetricLogger.for_test_case( 56 metric_name='sensitivity') 57 58 def setup_class(self): 59 """ Executed before any test case is started. Initializes the Contest 60 controller and prepares the DUT for testing. """ 61 62 req_params = [ 63 self.CONTEST_IP_KEY, self.REMOTE_SERVER_PORT_KEY, 64 self.AUTOMATION_PORT_KEY, self.AUTOMATION_LISTEN_IP, 65 self.FTP_USER_KEY, self.FTP_PASSWORD_KEY 66 ] 67 68 for param in req_params: 69 if param not in self.user_params: 70 self.log.error('Required parameter {} is missing in config ' 71 'file.'.format(param)) 72 return False 73 74 contest_ip = self.user_params[self.CONTEST_IP_KEY] 75 remote_port = self.user_params[self.REMOTE_SERVER_PORT_KEY] 76 automation_port = self.user_params[self.AUTOMATION_PORT_KEY] 77 listen_ip = self.user_params[self.AUTOMATION_LISTEN_IP] 78 ftp_user = self.user_params[self.FTP_USER_KEY] 79 ftp_password = self.user_params[self.FTP_PASSWORD_KEY] 80 custom_files = self.user_params.get(self.CUSTOM_FILES_KEY, []) 81 82 self.dut = self.android_devices[0] 83 84 self.contest = contest.Contest(logger=self.log, 85 remote_ip=contest_ip, 86 remote_port=remote_port, 87 automation_listen_ip=listen_ip, 88 automation_port=automation_port, 89 dut_on_func=self.set_apm_off, 90 dut_off_func=self.set_apm_on, 91 ftp_usr=ftp_user, 92 ftp_pwd=ftp_password) 93 94 # Look for the threshold files 95 for file in custom_files: 96 if 'pass_fail_threshold_' + self.dut.model in file: 97 self.thresholds_file = file 98 self.log.debug('Threshold file loaded: ' + file) 99 break 100 else: 101 self.log.warning('No threshold files found in custom files.') 102 103 def teardown_class(self): 104 """ Executed after completing all selected test cases.""" 105 if self.contest: 106 self.contest.destroy() 107 108 def setup_test(self): 109 """ Executed before every test case. 110 111 Returns: 112 False if the setup failed. 113 """ 114 115 testplan_formatted_key = self.TESTPLAN_KEY.format(self.test_name) 116 117 if testplan_formatted_key not in self.user_params: 118 self.log.error('Test plan not indicated in the config file. Use ' 119 'the {} key to set the testplan filename.'.format( 120 testplan_formatted_key)) 121 return False 122 123 self.testplan = self.user_params[testplan_formatted_key] 124 125 def agnss_performance_test(self): 126 """ Executes the aGNSS performance test and verifies that the results 127 are within the expected values if a thresholds file is available. 128 129 The thresholds file is in json format and contains the metrics keys 130 defined in the Contest object with 'min' and 'max' values. """ 131 132 results = self.contest.execute_testplan(self.testplan) 133 134 asserts.assert_true( 135 results, 'No results were obtained from the test execution.') 136 137 if not self.thresholds_file: 138 self.log.info('Skipping pass / fail check because no thresholds ' 139 'file was provided.') 140 return 141 142 passed = True 143 144 with open(self.thresholds_file, 'r') as file: 145 146 thresholds = json.load(file) 147 148 for key, val in results.items(): 149 150 asserts.assert_true( 151 key in thresholds, 'Key {} is missing in ' 152 'the thresholds file.'.format(key)) 153 154 # If the result is provided as a dictionary, obtain the value 155 # from the 'avg' key. 156 if isinstance(val, dict): 157 metric = val['avg'] 158 else: 159 metric = val 160 161 if thresholds[key]['min'] < metric < thresholds[key]['max']: 162 self.log.info('Metric {} = {} is within the expected ' 163 'values.'.format(key, metric)) 164 else: 165 self.log.error('Metric {} = {} is not within ({}, ' 166 '{}).'.format(key, metric, 167 thresholds[key]['min'], 168 thresholds[key]['max'])) 169 passed = False 170 171 # Save metric to Blackbox logger 172 if key == contest.Contest.TTFF_KEY: 173 self.ttf_metric.metric_value = metric 174 elif key == contest.Contest.POS_ERROR_KEY: 175 self.pos_error_metric.metric_value = metric 176 elif key == contest.Contest.SENSITIVITY_KEY: 177 self.sensitivity_metric.metric_value = metric 178 179 asserts.assert_true( 180 passed, 'At least one of the metrics was not ' 181 'within the expected values.') 182 183 def set_apm_on(self): 184 """ Wrapper method to turn airplane mode on. 185 186 This is passed to the Contest object so it can be executed when the 187 automation system requires the DUT to be set to 'off' state. 188 """ 189 190 tel_test_utils.toggle_airplane_mode(self.log, self.dut, True) 191 192 def set_apm_off(self): 193 """ Wrapper method to turn airplane mode off. 194 195 This is passed to the Contest object so it can be executed when the 196 automation system requires the DUT to be set to 'on' state. 197 """ 198 # Wait for the Contest system to initialize the base stations before 199 # actually setting APM off. 200 time.sleep(5) 201 202 tel_test_utils.toggle_airplane_mode(self.log, self.dut, False) 203 204 def test_agnss_performance(self): 205 self.agnss_performance_test() 206