| #!/usr/bin/env python3 |
| # |
| # Copyright 2020 - The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the 'License'); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an 'AS IS' BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import os |
| import time |
| import glob |
| import errno |
| from collections import namedtuple |
| from pandas import DataFrame |
| from acts import utils |
| from acts import signals |
| from acts.base_test import BaseTestClass |
| from acts.controllers.gnss_lib import GnssSimulator |
| from acts.context import get_current_context |
| from acts_contrib.test_utils.gnss import dut_log_test_utils as diaglog |
| from acts_contrib.test_utils.gnss import gnss_test_utils as gutils |
| from acts_contrib.test_utils.gnss import gnss_testlog_utils as glogutils |
| from acts_contrib.test_utils.gnss.gnss_defines import DEVICE_GPSLOG_FOLDER |
| from acts_contrib.test_utils.gnss.gnss_defines import GPS_PKG_NAME |
| from acts_contrib.test_utils.gnss.gnss_defines import BCM_GPS_XML_PATH |
| |
| |
| class LabTtffTestBase(BaseTestClass): |
| """ LAB TTFF Tests Base Class""" |
| GTW_GPSTOOL_APP = 'gtw_gpstool_apk' |
| GNSS_SIMULATOR_KEY = 'gnss_simulator' |
| GNSS_SIMULATOR_IP_KEY = 'gnss_simulator_ip' |
| GNSS_SIMULATOR_PORT_KEY = 'gnss_simulator_port' |
| GNSS_SIMULATOR_PORT_CTRL_KEY = 'gnss_simulator_port_ctrl' |
| GNSS_SIMULATOR_SCENARIO_KEY = 'gnss_simulator_scenario' |
| GNSS_SIMULATOR_POWER_LEVEL_KEY = 'gnss_simulator_power_level' |
| CUSTOM_FILES_KEY = 'custom_files' |
| CSTTFF_CRITERIA = 'cs_criteria' |
| HSTTFF_CRITERIA = 'hs_criteria' |
| WSTTFF_CRITERIA = 'ws_criteria' |
| CSTTFF_PECRITERIA = 'cs_ttff_pecriteria' |
| HSTTFF_PECRITERIA = 'hs_ttff_pecriteria' |
| WSTTFF_PECRITERIA = 'ws_ttff_pecriteria' |
| TTFF_ITERATION = 'ttff_iteration' |
| SIMULATOR_LOCATION = 'simulator_location' |
| DIAG_OPTION = 'diag_option' |
| |
| def __init__(self, controllers): |
| """ Initializes class attributes. """ |
| |
| super().__init__(controllers) |
| |
| self.dut = None |
| self.gnss_simulator = None |
| self.rockbottom_script = None |
| self.gnss_log_path = self.log_path |
| self.gps_xml_bk_path = BCM_GPS_XML_PATH + '.bk' |
| |
| def setup_class(self): |
| super().setup_class() |
| |
| req_params = [ |
| self.GNSS_SIMULATOR_KEY, self.GNSS_SIMULATOR_IP_KEY, |
| self.GNSS_SIMULATOR_PORT_KEY, self.GNSS_SIMULATOR_SCENARIO_KEY, |
| self.GNSS_SIMULATOR_POWER_LEVEL_KEY, self.CSTTFF_CRITERIA, |
| self.HSTTFF_CRITERIA, self.WSTTFF_CRITERIA, self.TTFF_ITERATION, |
| self.SIMULATOR_LOCATION, self.DIAG_OPTION |
| ] |
| |
| self.unpack_userparams(req_param_names=req_params) |
| self.dut = self.android_devices[0] |
| self.gnss_simulator_scenario = self.user_params[ |
| self.GNSS_SIMULATOR_SCENARIO_KEY] |
| self.gnss_simulator_power_level = self.user_params[ |
| self.GNSS_SIMULATOR_POWER_LEVEL_KEY] |
| self.gtw_gpstool_app = self.user_params[self.GTW_GPSTOOL_APP] |
| custom_files = self.user_params.get(self.CUSTOM_FILES_KEY, []) |
| self.cs_ttff_criteria = self.user_params.get(self.CSTTFF_CRITERIA, []) |
| self.hs_ttff_criteria = self.user_params.get(self.HSTTFF_CRITERIA, []) |
| self.ws_ttff_criteria = self.user_params.get(self.WSTTFF_CRITERIA, []) |
| self.cs_ttff_pecriteria = self.user_params.get(self.CSTTFF_PECRITERIA, |
| []) |
| self.hs_ttff_pecriteria = self.user_params.get(self.HSTTFF_PECRITERIA, |
| []) |
| self.ws_ttff_pecriteria = self.user_params.get(self.WSTTFF_PECRITERIA, |
| []) |
| self.ttff_iteration = self.user_params.get(self.TTFF_ITERATION, []) |
| self.simulator_location = self.user_params.get(self.SIMULATOR_LOCATION, |
| []) |
| self.diag_option = self.user_params.get(self.DIAG_OPTION, []) |
| |
| # Create gnss_simulator instance |
| gnss_simulator_key = self.user_params[self.GNSS_SIMULATOR_KEY] |
| gnss_simulator_ip = self.user_params[self.GNSS_SIMULATOR_IP_KEY] |
| gnss_simulator_port = self.user_params[self.GNSS_SIMULATOR_PORT_KEY] |
| if gnss_simulator_key == 'gss7000': |
| gnss_simulator_port_ctrl = self.user_params[ |
| self.GNSS_SIMULATOR_PORT_CTRL_KEY] |
| else: |
| gnss_simulator_port_ctrl = None |
| self.gnss_simulator = GnssSimulator.AbstractGnssSimulator( |
| gnss_simulator_key, gnss_simulator_ip, gnss_simulator_port, |
| gnss_simulator_port_ctrl) |
| |
| test_type = namedtuple('Type', ['command', 'criteria']) |
| self.test_types = { |
| 'cs': test_type('Cold Start', self.cs_ttff_criteria), |
| 'ws': test_type('Warm Start', self.ws_ttff_criteria), |
| 'hs': test_type('Hot Start', self.hs_ttff_criteria) |
| } |
| |
| # Unpack the rockbottom script file if its available. |
| for file in custom_files: |
| if 'rockbottom_' + self.dut.model in file: |
| self.rockbottom_script = file |
| break |
| |
| def setup_test(self): |
| |
| self.clear_gps_log() |
| self.gnss_simulator.stop_scenario() |
| self.gnss_simulator.close() |
| if self.rockbottom_script: |
| self.log.info('Running rockbottom script for this device ' + |
| self.dut.model) |
| self.dut_rockbottom() |
| else: |
| self.log.info('Not running rockbottom for this device ' + |
| self.dut.model) |
| |
| utils.set_location_service(self.dut, True) |
| gutils.reinstall_package_apk(self.dut, GPS_PKG_NAME, |
| self.gtw_gpstool_app) |
| |
| # For BCM DUTs, delete gldata.sto and set IgnoreRomAlm="true" based on b/196936791#comment20 |
| if self.diag_option == "BCM": |
| gutils.remount_device(self.dut) |
| # Backup gps.xml |
| copy_cmd = "cp {} {}".format(BCM_GPS_XML_PATH, self.gps_xml_bk_path) |
| self.dut.adb.shell(copy_cmd) |
| gutils.delete_bcm_nvmem_sto_file(self.dut) |
| gutils.bcm_gps_ignore_rom_alm(self.dut) |
| # Reboot DUT to apply the setting |
| gutils.reboot(self.dut) |
| self.gnss_simulator.connect() |
| |
| def dut_rockbottom(self): |
| """ |
| Set the dut to rockbottom state |
| |
| """ |
| # The rockbottom script might include a device reboot, so it is |
| # necessary to stop SL4A during its execution. |
| self.dut.stop_services() |
| self.log.info('Executing rockbottom script for ' + self.dut.model) |
| os.chmod(self.rockbottom_script, 0o777) |
| os.system('{} {}'.format(self.rockbottom_script, self.dut.serial)) |
| # Make sure the DUT is in root mode after coming back |
| self.dut.root_adb() |
| # Restart SL4A |
| self.dut.start_services() |
| |
| def teardown_test(self): |
| """Teardown settings for the test class""" |
| super().teardown_test() |
| # Restore the gps.xml everytime after the test. |
| if self.diag_option == "BCM": |
| # Restore gps.xml |
| rm_cmd = "rm -rf {}".format(BCM_GPS_XML_PATH) |
| restore_cmd = "mv {} {}".format(self.gps_xml_bk_path, |
| BCM_GPS_XML_PATH) |
| self.dut.adb.shell(rm_cmd) |
| self.dut.adb.shell(restore_cmd) |
| |
| def teardown_class(self): |
| """ Executed after completing all selected test cases.""" |
| self.clear_gps_log() |
| if self.gnss_simulator: |
| self.gnss_simulator.stop_scenario() |
| self.gnss_simulator.close() |
| |
| def start_and_set_gnss_simulator_power(self): |
| """ |
| Start GNSS simulator secnario and set power level. |
| |
| """ |
| |
| self.gnss_simulator.start_scenario(self.gnss_simulator_scenario) |
| time.sleep(25) |
| self.gnss_simulator.set_power(self.gnss_simulator_power_level) |
| |
| def get_and_verify_ttff(self, mode): |
| """Retrieve ttff with designate mode. |
| |
| Args: |
| mode: A string for identify gnss test mode. |
| """ |
| if mode not in self.test_types: |
| raise signals.TestError('Unrecognized mode %s' % mode) |
| test_type = self.test_types.get(mode) |
| |
| if mode != 'cs': |
| wait_time = 900 |
| else: |
| wait_time = 300 |
| |
| gutils.process_gnss_by_gtw_gpstool(self.dut, |
| self.test_types['cs'].criteria) |
| begin_time = gutils.get_current_epoch_time() |
| gutils.start_ttff_by_gtw_gpstool(self.dut, |
| ttff_mode=mode, |
| iteration=self.ttff_iteration, |
| raninterval=True, |
| hot_warm_sleep=wait_time) |
| ttff_data = gutils.process_ttff_by_gtw_gpstool(self.dut, begin_time, |
| self.simulator_location) |
| |
| # Create folder for GTW GPStool's log |
| gps_log_path = os.path.join(self.gnss_log_path, 'GPSLogs') |
| os.makedirs(gps_log_path, exist_ok=True) |
| |
| self.dut.adb.pull("{} {}".format(DEVICE_GPSLOG_FOLDER, gps_log_path)) |
| |
| gps_api_log = glob.glob(gps_log_path + '/*/GNSS_*.txt') |
| ttff_loop_log = glob.glob(gps_log_path + |
| '/*/GPS_{}_*.txt'.format(mode.upper())) |
| |
| if not gps_api_log and ttff_loop_log: |
| raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), |
| gps_log_path) |
| |
| df = DataFrame(glogutils.parse_gpstool_ttfflog_to_df(gps_api_log[0])) |
| |
| ttff_dict = {} |
| for i in ttff_data: |
| d = ttff_data[i]._asdict() |
| ttff_dict[i] = dict(d) |
| |
| ttff_time = [] |
| ttff_pe = [] |
| for i in ttff_dict.keys(): |
| ttff_time.append(ttff_dict[i]['ttff_sec']) |
| ttff_pe.append(ttff_dict[i]['ttff_pe']) |
| df['ttff_sec'] = ttff_time |
| df['ttff_pe'] = ttff_pe |
| df.to_json(gps_log_path + '/gps_log.json', orient='table') |
| result = gutils.check_ttff_data(self.dut, |
| ttff_data, |
| ttff_mode=test_type.command, |
| criteria=test_type.criteria) |
| if not result: |
| raise signals.TestFailure('%s TTFF fails to reach ' |
| 'designated criteria' % test_type.command) |
| return ttff_data |
| |
| def verify_pe(self, mode): |
| """ |
| Verify ttff Position Error with designate mode. |
| |
| Args: |
| mode: A string for identify gnss test mode. |
| """ |
| |
| ffpe_type = namedtuple('Type', ['command', 'pecriteria']) |
| ffpe_types = { |
| 'cs': ffpe_type('Cold Start', self.cs_ttff_pecriteria), |
| 'ws': ffpe_type('Warm Start', self.ws_ttff_pecriteria), |
| 'hs': ffpe_type('Hot Start', self.hs_ttff_pecriteria) |
| } |
| |
| if mode not in ffpe_types: |
| raise signals.TestError('Unrecognized mode %s' % mode) |
| test_type = ffpe_types.get(mode) |
| |
| ttff_data = self.get_and_verify_ttff(mode) |
| result = gutils.check_ttff_pe(self.dut, |
| ttff_data, |
| ttff_mode=test_type.command, |
| pe_criteria=test_type.pecriteria) |
| if not result: |
| raise signals.TestFailure('%s TTFF fails to reach ' |
| 'designated criteria' % test_type.command) |
| return ttff_data |
| |
| def clear_gps_log(self): |
| """ |
| Delete the existing GPS GTW Log from DUT. |
| |
| """ |
| self.dut.adb.shell("rm -rf {}".format(DEVICE_GPSLOG_FOLDER)) |
| |
| def gnss_ttff_ffpe(self, mode, sub_context_path=''): |
| """ |
| Base ttff and ffpe function |
| Args: |
| mode: Set the TTFF mode for testing. Definitions are as below. |
| cs(cold start), ws(warm start), hs(hot start) |
| sub_context_path: Set specifc log pathfor ttff_ffpe |
| """ |
| # Create log file path |
| full_output_path = get_current_context().get_full_output_path() |
| self.gnss_log_path = os.path.join(full_output_path, sub_context_path) |
| os.makedirs(self.gnss_log_path, exist_ok=True) |
| self.log.debug('Create log path: {}'.format(self.gnss_log_path)) |
| |
| # Start and set GNSS simulator |
| self.start_and_set_gnss_simulator_power() |
| |
| # Start GNSS chip log |
| if self.diag_option == "QCOM": |
| diaglog.start_diagmdlog_background(self.dut, maskfile=self.maskfile) |
| else: |
| gutils.start_pixel_logger(self.dut) |
| |
| # Start verifying TTFF and FFPE |
| self.verify_pe(mode) |
| |
| # Set gnss_vendor_log_path based on GNSS solution vendor |
| gnss_vendor_log_path = os.path.join(self.gnss_log_path, |
| self.diag_option) |
| os.makedirs(gnss_vendor_log_path, exist_ok=True) |
| |
| # Stop GNSS chip log and pull the logs to local file system |
| if self.diag_option == "QCOM": |
| diaglog.stop_background_diagmdlog(self.dut, |
| gnss_vendor_log_path, |
| keep_logs=False) |
| else: |
| gutils.stop_pixel_logger(self.dut) |
| self.log.info('Getting Pixel BCM Log!') |
| diaglog.get_pixellogger_bcm_log(self.dut, |
| gnss_vendor_log_path, |
| keep_logs=False) |