From bb33123b17832a2c4b558432e398fc3701453c07 Mon Sep 17 00:00:00 2001 From: muendelezaji Date: Wed, 22 Jun 2016 19:34:04 +0100 Subject: [PATCH] Check APK version and ABI when installing - Check the APK's versionName property against the workload's expected version if specified - If workload specifies check_abi param, try to get APK from ABI-specific path on the host - Add variant_name param to APK resource-getter for backwards compatibility of dex2oat and peacekeeper --- wlauto/common/android/device.py | 4 +- wlauto/common/android/resources.py | 7 +++ wlauto/common/android/workload.py | 18 ++++++-- wlauto/resource_getters/standard.py | 59 ++++++++++++++++++++---- wlauto/workloads/dex2oat/__init__.py | 4 +- wlauto/workloads/peacekeeper/__init__.py | 2 +- 6 files changed, 76 insertions(+), 18 deletions(-) diff --git a/wlauto/common/android/device.py b/wlauto/common/android/device.py index 0db61fb3..a269652a 100644 --- a/wlauto/common/android/device.py +++ b/wlauto/common/android/device.py @@ -362,9 +362,9 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 if ext == '.apk': flags = [] if replace: - flags.append('-r') # Replace existing APK + flags.append('-r') # Replace existing APK if self.get_sdk_version() >= 23: - flags.append('-g') # Grant all runtime permissions + flags.append('-g') # Grant all runtime permissions self.logger.debug("Replace APK = {}, ADB flags = '{}'".format(replace, ' '.join(flags))) return adb_command(self.adb_name, "install {} '{}'".format(' '.join(flags), filepath), timeout=timeout) else: diff --git a/wlauto/common/android/resources.py b/wlauto/common/android/resources.py index 27231e16..238df045 100644 --- a/wlauto/common/android/resources.py +++ b/wlauto/common/android/resources.py @@ -34,3 +34,10 @@ class JarFile(FileResource): class ApkFile(FileResource): name = 'apk' + + def __init__(self, owner, platform=None): + super(ApkFile, self).__init__(owner) + self.platform = platform + + def __str__(self): + return '<{}\'s {} APK>'.format(self.owner, self.platform) diff --git a/wlauto/common/android/workload.py b/wlauto/common/android/workload.py index 2033bc32..3c5268c9 100644 --- a/wlauto/common/android/workload.py +++ b/wlauto/common/android/workload.py @@ -20,6 +20,7 @@ import time from wlauto.core.extension import Parameter from wlauto.core.workload import Workload from wlauto.core.resource import NO_ONE +from wlauto.common.android.resources import ApkFile from wlauto.common.resources import ExtensionAsset, Executable from wlauto.exceptions import WorkloadError, ResourceError, ConfigError from wlauto.utils.android import ApkInfo, ANDROID_NORMAL_PERMISSIONS @@ -160,6 +161,11 @@ class ApkWorkload(Workload): '''), Parameter('uninstall_apk', kind=boolean, default=False, description='If ``True``, will uninstall workload\'s APK as part of teardown.'), + Parameter('check_abi', kind=bool, default=False, + description=''' + If ``True``, workload will check that the APK matches the target + device ABI, otherwise any APK found will be used. + '''), ] def __init__(self, device, _call_super=True, **kwargs): @@ -169,12 +175,14 @@ class ApkWorkload(Workload): self.apk_version = None self.logcat_log = None - def init_resources(self, context): - self.apk_file = context.resolver.get(wlauto.common.android.resources.ApkFile(self), + def initialize(self, context): + # Get APK for the correct version and device ABI + self.apk_file = context.resolver.get(ApkFile(self, self.device.abi), version=getattr(self, 'version', None), + check_abi=getattr(self, 'check_abi', False), + variant_name=getattr(self, 'variant_name', None), strict=self.check_apk) - - def validate(self): + # Validate the APK if self.check_apk: if not self.apk_file: raise WorkloadError('No APK file found for workload {}.'.format(self.name)) @@ -223,7 +231,7 @@ class ApkWorkload(Workload): if installed_version: self.device.uninstall(self.package) # It's possible that the uninstall above fails, which might result in a warning - # and/or failure during installation. Hower execution should proceed, so need + # and/or failure during installation. However execution should proceed, so need # to make sure that the right apk_vesion is reported in the end. if self.install_apk(context): self.apk_version = host_version diff --git a/wlauto/resource_getters/standard.py b/wlauto/resource_getters/standard.py index 17cbf64c..50b91559 100644 --- a/wlauto/resource_getters/standard.py +++ b/wlauto/resource_getters/standard.py @@ -30,6 +30,7 @@ import requests from wlauto import ResourceGetter, GetterPriority, Parameter, NO_ONE, settings, __file__ as __base_filepath from wlauto.exceptions import ResourceError +from wlauto.utils.android import ApkInfo from wlauto.utils.misc import ensure_directory_exists as _d, ensure_file_directory_exists as _f, sha256, urljoin from wlauto.utils.types import boolean @@ -61,9 +62,11 @@ class PackageFileGetter(ResourceGetter): class EnvironmentFileGetter(ResourceGetter): name = 'environment_file' - description = """Looks for exactly one file with the specified extension in the owner's directory. If a version + description = """ + Looks for exactly one file with the specified extension in the owner's directory. If a version is specified on invocation of get, it will filter the discovered file based on that version. - Versions are treated as case-insensitive.""" + Versions are treated as case-insensitive. + """ extension = None @@ -102,6 +105,22 @@ class PackageApkGetter(PackageFileGetter): name = 'package_apk' extension = 'apk' + description = """ + Uses the same dependency resolution mechanism as ``PackageFileGetter`` with one addition. + If an ABI is specified in the resource request, then the getter will try to locate the file in + the ABI-specific folder in the form ``/apk//``. Where ``root`` is the base + resource location e.g. ``~/.workload_automation/dependencies/`` and ```` + is the ABI for which the APK has been compiled, as returned by ``resource.platform``. + """ + + def get(self, resource, **kwargs): + resource_dir = os.path.dirname(sys.modules[resource.owner.__module__].__file__) + version = kwargs.get('version') + variant = kwargs.get('variant_name') + if kwargs.get('check_abi', False): + resource_dir = os.path.join(resource_dir, self.extension, resource.platform) + return get_from_location_by_extension(resource, resource_dir, self.extension, version, variant=variant) + class PackageJarGetter(PackageFileGetter): name = 'package_jar' @@ -120,6 +139,22 @@ class EnvironmentApkGetter(EnvironmentFileGetter): name = 'environment_apk' extension = 'apk' + description = """ + Uses the same dependency resolution mechanism as ``EnvironmentFileGetter`` with one addition. + If an ABI is specified in the resource request, then the getter will try to locate the file in + the ABI-specific folder in the form ``/apk//``. Where ``root`` is the base + resource location e.g. ``~/.workload_automation/dependencies/`` and ```` + is the ABI for which the APK has been compiled, as returned by ``resource.platform``. + """ + + def get(self, resource, **kwargs): + resource_dir = resource.owner.dependencies_directory + version = kwargs.get('version') + variant = kwargs.get('variant_name') + if kwargs.get('check_abi', False): + resource_dir = os.path.join(resource_dir, self.extension, resource.platform) + return get_from_location_by_extension(resource, resource_dir, self.extension, version, variant=variant) + class EnvironmentJarGetter(EnvironmentFileGetter): name = 'environment_jar' @@ -427,6 +462,10 @@ class RemoteFilerGetter(ResourceGetter): if resource.owner: remote_path = os.path.join(self.remote_path, resource.owner.name) local_path = os.path.join(settings.environment_root, '__filer', resource.owner.dependencies_directory) + if resource.name == 'apk' and kwargs.get('check_abi', False): + local_path = os.path.join(local_path, 'apk', resource.platform) + message = 'resource={}, version={}, remote_path={}, local_path={}' + self.logger.debug(message.format(resource, version, remote_path, local_path)) return self.try_get_resource(resource, version, remote_path, local_path) else: result = None @@ -489,24 +528,28 @@ class RemoteFilerGetter(ResourceGetter): # Utility functions -def get_from_location_by_extension(resource, location, extension, version=None): +def get_from_location_by_extension(resource, location, extension, version=None, variant=None): try: found_files = [os.path.join(location, f) for f in os.listdir(location)] except OSError: return None try: - return get_from_list_by_extension(resource, found_files, extension, version) + return get_from_list_by_extension(resource, found_files, extension, version, variant=variant) except ResourceError: raise ResourceError('More than one .{} found in {} for {}.'.format(extension, location, resource.owner.name)) -def get_from_list_by_extension(resource, filelist, extension, version=None): - filelist = [ff for ff in filelist - if os.path.splitext(ff)[1].lower().endswith(extension)] +def get_from_list_by_extension(resource, filelist, extension, version=None, variant=None): + filelist = [ff for ff in filelist if os.path.splitext(ff)[1].lower().endswith(extension)] + if variant: + filelist = [ff for ff in filelist if variant.lower() in os.path.basename(ff).lower()] if version: - filelist = [ff for ff in filelist if version.lower() in os.path.basename(ff).lower()] + if extension == 'apk': + filelist = [ff for ff in filelist if version.lower() in ApkInfo(ff).version_name.lower()] + else: + filelist = [ff for ff in filelist if version.lower() in os.path.basename(ff).lower()] if len(filelist) == 1: return filelist[0] elif not filelist: diff --git a/wlauto/workloads/dex2oat/__init__.py b/wlauto/workloads/dex2oat/__init__.py index 4d1955bb..40621ce6 100644 --- a/wlauto/workloads/dex2oat/__init__.py +++ b/wlauto/workloads/dex2oat/__init__.py @@ -53,7 +53,8 @@ class Dex2oatBenchmark(Workload): def init_resources(self, context): # TODO: find a better APK to use for this. peacekeeper = ExtensionLoader().get_workload('peacekeeper', self.device) - self.apk_file = context.resolver.get(wlauto.common.android.resources.ApkFile(peacekeeper), version='chrome') + self.apk_file = context.resolver.get(wlauto.common.android.resources.ApkFile(peacekeeper), + variant_name='chrome') self.package = ApkInfo(self.apk_file).package def setup(self, context): @@ -119,4 +120,3 @@ class Dex2oatBenchmark(Workload): def teardown(self, context): self.device.delete_file(self.on_device_oat) - diff --git a/wlauto/workloads/peacekeeper/__init__.py b/wlauto/workloads/peacekeeper/__init__.py index 61c65f7c..ad23e09e 100644 --- a/wlauto/workloads/peacekeeper/__init__.py +++ b/wlauto/workloads/peacekeeper/__init__.py @@ -63,7 +63,7 @@ class Peacekeeper(AndroidUiAutoBenchmark): def __init__(self, device, **kwargs): super(Peacekeeper, self).__init__(device, **kwargs) - self.version = self.browser + self.variant_name = self.browser def update_result(self, context): super(Peacekeeper, self).update_result(context)