| #!/usr/bin/env python3 |
| # |
| # Copyright 2019 - 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 yaml |
| from acts.keys import Config |
| from acts.test_utils.instrumentation import app_installer |
| from acts.test_utils.instrumentation import config_wrapper |
| from acts.test_utils.instrumentation.instrumentation_command_builder import \ |
| InstrumentationCommandBuilder |
| from acts.test_utils.instrumentation.adb_commands import common |
| |
| from acts import base_test |
| |
| RESOLVE_FILE_MARKER = 'FILE' |
| FILE_NOT_FOUND = 'File is missing from ACTS config' |
| DEFAULT_POWER_CONFIG_FILE = 'power_config.yaml' |
| |
| |
| class InstrumentationTestError(Exception): |
| pass |
| |
| |
| class InstrumentationBaseTest(base_test.BaseTestClass): |
| """Base class for power tests based on am instrument.""" |
| |
| def __init__(self, configs): |
| """Initialize an InstrumentationBaseTest |
| |
| Args: |
| configs: Dict representing the test configuration |
| """ |
| super().__init__(configs) |
| # Take power config path directly from ACTS config if found, otherwise |
| # try to find the power config in the same directory as the ACTS config |
| power_config_path = '' |
| if 'power_config' in self.user_params: |
| power_config_path = self.user_params['power_config'][0] |
| elif Config.key_config_path.value in self.user_params: |
| power_config_path = os.path.join( |
| self.user_params[Config.key_config_path.value], |
| DEFAULT_POWER_CONFIG_FILE) |
| self._power_config = None |
| if os.path.exists(power_config_path): |
| self._power_config = self._load_power_config(power_config_path) |
| else: |
| self.log.warning( |
| 'Power config file %s does not exist' % power_config_path) |
| |
| def _load_power_config(self, path): |
| """Load the power config file into a InstrumentationConfigWrapper |
| object. |
| |
| Args: |
| path: Path to the power config file. |
| |
| Returns: The loaded power config as an InstrumentationConfigWrapper |
| """ |
| try: |
| with open(path, mode='r', encoding='utf-8') as f: |
| config_dict = yaml.safe_load(f) |
| except Exception as e: |
| raise InstrumentationTestError( |
| 'Cannot open or parse power config file %s' % path) from e |
| if not self._resolve_file_paths(config_dict): |
| self.log.warning('File paths missing from power config.') |
| |
| # Write out a copy of the resolved power config |
| with open(os.path.join(self.log_path, 'resolved_power_config.yaml'), |
| mode='w', encoding='utf-8') as f: |
| yaml.safe_dump(config_dict, f) |
| |
| return config_wrapper.ConfigWrapper(config_dict) |
| |
| def _resolve_file_paths(self, config): |
| """Recursively resolve all 'FILE' markers found in the power config to |
| their corresponding paths in the ACTS config, i.e. in self.user_params. |
| |
| Args: |
| config: The power config to update |
| |
| Returns: True if all 'FILE' markers are resolved. |
| """ |
| success = True |
| for key, value in config.items(): |
| # Recursive call; resolve files in nested maps |
| if isinstance(value, dict): |
| success &= self._resolve_file_paths(value) |
| # Replace file resolver markers with paths from ACTS config |
| elif value == RESOLVE_FILE_MARKER: |
| if key not in self.user_params: |
| success = False |
| config[key] = FILE_NOT_FOUND |
| else: |
| config[key] = self.user_params[key] |
| return success |
| |
| def _prepare_device(self): |
| """Prepares the device for testing.""" |
| pass |
| |
| def setup_class(self): |
| """Class setup""" |
| self.ad_dut = self.android_devices[0] |
| self.ad_apps = app_installer.AppInstaller(self.ad_dut) |
| self._prepare_device() |
| |
| def adb_run(self, cmds, non_blocking=False): |
| """Run the specified command, or list of commands, with the ADB shell. |
| |
| Args: |
| cmds: A string or list of strings representing ADB shell command(s) |
| non_blocking: Run asynchronously |
| |
| Returns: dict mapping command to resulting stdout |
| """ |
| if isinstance(cmds, str): |
| cmds = [cmds] |
| adb = self.ad_dut.adb |
| adb_shell = adb.shell_nb if non_blocking else adb.shell |
| out = {} |
| for cmd in cmds: |
| out[cmd] = adb_shell(cmd) |
| return out |
| |
| # Basic preparer methods |
| |
| def enable_airplane_mode(self): |
| """Run the set of commands to properly enable airplane mode.""" |
| self.adb_run(common.airplane_mode.toggle(True)) |
| self.adb_run(common.auto_time.toggle(False)) |
| self.adb_run(common.auto_timezone.toggle(False)) |
| self.adb_run(common.location_gps.toggle(False)) |
| self.adb_run(common.location_network.toggle(False)) |
| self.adb_run(common.wifi.toggle(False)) |
| self.adb_run(common.bluetooth.toggle(False)) |
| |
| def grant_permissions(self, permissions_apk_path): |
| """Grant all runtime permissions with PermissionUtils. |
| |
| Args: |
| permissions_apk_path: Path to PermissionUtils.apk |
| """ |
| self.log.info('Granting all revoked runtime permissions.') |
| |
| # Install PermissionUtils.apk |
| self.ad_apps.install(permissions_apk_path) |
| if not self.ad_apps.is_installed(permissions_apk_path): |
| raise InstrumentationTestError( |
| 'Failed to install PermissionUtils.apk, abort!') |
| package_name = self.ad_apps.get_package_name(permissions_apk_path) |
| |
| # Run the instrumentation command |
| cmd_builder = InstrumentationCommandBuilder() |
| cmd_builder.set_manifest_package(package_name) |
| cmd_builder.set_runner('.PermissionInstrumentation') |
| cmd_builder.add_flag('-w') |
| cmd_builder.add_flag('-r') |
| cmd_builder.add_key_value_param('command', 'grant-all') |
| cmd = cmd_builder.build() |
| self.log.debug('Instrumentation call: %s' % cmd) |
| self.adb_run(cmd) |
| |
| # Uninstall PermissionUtils.apk |
| self.ad_apps.uninstall(permissions_apk_path) |