From 0057a531fdb0e0d069970f0bc991ef4dd01efe3b Mon Sep 17 00:00:00 2001 From: Sebastian Goscik Date: Thu, 25 Feb 2016 11:30:14 +0000 Subject: [PATCH] RecordCommand: Improved record command to handle workloads The wa record command now has the ability to record revent files for wa workload. The command with automatically deploy the workload and record both setup and run revent recordings Conflicts: wlauto/commands/record.py --- wlauto/commands/record.py | 124 +++++++++++++++++++++--------- wlauto/common/android/workload.py | 29 ++++--- 2 files changed, 109 insertions(+), 44 deletions(-) diff --git a/wlauto/commands/record.py b/wlauto/commands/record.py index 373099f9..e5f693df 100644 --- a/wlauto/commands/record.py +++ b/wlauto/commands/record.py @@ -16,11 +16,15 @@ import os import sys + +from wlauto import Command, settings +from wlauto.core import pluginloader from wlauto.common.resources import Executable from wlauto.core.resource import NO_ONE from wlauto.core.resolver import ResourceResolver from wlauto.core.configuration import RunConfiguration from wlauto.core.agenda import Agenda +from wlauto.common.android.workload import ApkWorkload class RecordCommand(Command): @@ -50,78 +54,126 @@ class RecordCommand(Command): def initialize(self, context): self.context = context self.parser.add_argument('-d', '--device', help='The name of the device') - self.parser.add_argument('-s', '--suffix', help='The suffix of the revent file, e.g. ``setup``') self.parser.add_argument('-o', '--output', help='Directory to save the recording in') - self.parser.add_argument('-p', '--package', help='Package to launch before recording') + + # Need validation + self.parser.add_argument('-s', '--suffix', help='The suffix of the revent file, e.g. ``setup``') self.parser.add_argument('-C', '--clear', help='Clear app cache before launching it', action="store_true") + group = self.parser.add_mutually_exclusive_group(required=False) + group.add_argument('-p', '--package', help='Package to launch before recording') + group.add_argument('-w', '--workload', help='Name of a revent workload (mostly games)') + # Validate command options def validate_args(self, args): - if args.clear and not args.package: - print "Package must be specified if you want to clear cache\n" + if args.clear and not (args.package or args.workload): + self.logger.error("Package/Workload must be specified if you want to clear cache") self.parser.print_help() sys.exit() + if args.workload and args.suffix: + self.logger.error("cannot specify manual suffixes for workloads") + self.parser.print_help() + sys.exit() + if args.suffix: + args.suffix += "." # pylint: disable=W0201 def execute(self, args): self.validate_args(args) self.logger.info("Connecting to device...") - ext_loader = PluginLoader(packages=settings.plugin_packages, - paths=settings.plugin_paths) - # Setup config - self.config = RunConfiguration(ext_loader) - for filepath in settings.get_config_paths(): + self.config = RunConfiguration(pluginloader) + for filepath in settings.config_paths: self.config.load_config(filepath) self.config.set_agenda(Agenda()) self.config.finalize() - context = LightContext(self.config) - # Setup device - self.device = ext_loader.get_device(settings.device, **settings.device_config) - self.device.validate() - self.device.connect() - self.device.initialize(context) - - host_binary = context.resolver.get(Executable(NO_ONE, self.device.abi, 'revent')) - self.target_binary = self.device.install_if_needed(host_binary) - - self.run(args) - - def run(self, args): + self.device_manager = pluginloader.get_manager(self.config.device) + self.device_manager.validate() + self.device_manager.connect() + context = LightContext(self.config, self.device_manager) + self.device_manager.initialize(context) + self.device = self.device_manager.target if args.device: self.device_name = args.device else: - self.device_name = self.device.get_device_model() + self.device_name = self.device.model - if args.suffix: - args.suffix += "." + # Install Revent + host_binary = context.resolver.get(Executable(NO_ONE, self.device.abi, 'revent')) + self.target_binary = self.device.install_if_needed(host_binary) - revent_file = self.device.path.join(self.device.working_directory, - '{}.{}revent'.format(self.device_name, args.suffix or "")) + if args.workload: + self.workload_record(args, context) + elif args.package: + self.package_record(args, context) + else: + self.manual_record(args, context) + def manual_record(self, args, context): + revent_file = self.device.get_workpath('{}.{}revent'.format(self.device_name, args.suffix or "")) + self._record(revent_file, "", args.output) + + def package_record(self, args, context): + revent_file = self.device.get_workpath('{}.{}revent'.format(self.device_name, args.suffix or "")) if args.clear: self.device.execute("pm clear {}".format(args.package)) - if args.package: - self.logger.info("Starting {}".format(args.package)) - self.device.execute('monkey -p {} -c android.intent.category.LAUNCHER 1'.format(args.package)) + self.logger.info("Starting {}".format(args.package)) + self.device.execute('monkey -p {} -c android.intent.category.LAUNCHER 1'.format(args.package)) - self.logger.info("Press Enter when you are ready to record...") + self._record(revent_file, "", args.output) + + def workload_record(self, args, context): + setup_file = self.device.get_workpath('{}.setup.revent'.format(self.device_name)) + run_file = self.device.get_workpath('{}.run.revent'.format(self.device_name)) + + self.logger.info("Deploying {}".format(args.workload)) + workload = pluginloader.get_workload(args.workload, self.device) + workload.apk_init_resources(context) + workload.initialize_package(context) + workload.do_post_install(context) + workload.start_activity() + + if args.clear: + workload.reset(context) + + self._record(setup_file, " SETUP", + args.output or os.path.join(workload.dependencies_directory, 'revent_files')) + self._record(run_file, " RUN", + args.output or os.path.join(workload.dependencies_directory, 'revent_files')) + + self.logger.info("Tearing down {}".format(args.workload)) + workload.apk_teardown(context) + + def _record(self, revent_file, name, output_path): + self.logger.info("Press Enter when you are ready to record{}...".format(name)) raw_input("") command = "{} record -t 100000 -s {}".format(self.target_binary, revent_file) self.device.kick_off(command) - self.logger.info("Press Enter when you have finished recording...") + self.logger.info("Press Enter when you have finished recording {}...".format(name)) raw_input("") self.device.killall("revent") - self.logger.info("Pulling files from device") - self.device.pull(revent_file, args.output or os.getcwdu()) + output_path = output_path or os.getcwdu() + if not os.path.isdir(output_path): + os.mkdirs(output_path) + revent_file_name = self.device.path.basename(revent_file) + host_path = os.path.join(output_path, revent_file_name) + if os.path.exists(host_path): + self.logger.info("Revent file '{}' already exists, overwrite? [y/n]".format(revent_file_name)) + if raw_input("") == "y": + os.remove(host_path) + else: + self.logger.warning("Did not pull and overwrite '{}'".format(revent_file_name)) + return + self.logger.info("Pulling '{}' from device".format(self.device.path.basename(revent_file))) + self.device.pull(revent_file, output_path) class ReplayCommand(RecordCommand): @@ -139,6 +191,7 @@ class ReplayCommand(RecordCommand): self.parser.add_argument('-C', '--clear', help='Clear app cache before launching it', action="store_true") + # pylint: disable=W0201 def run(self, args): self.logger.info("Pushing file to device") @@ -159,6 +212,7 @@ class ReplayCommand(RecordCommand): # Used to satisfy the API class LightContext(object): - def __init__(self, config): + def __init__(self, config, device_manager): self.resolver = ResourceResolver(config) self.resolver.load() + self.device_manager = device_manager diff --git a/wlauto/common/android/workload.py b/wlauto/common/android/workload.py index 69d4a5bf..bee513ec 100644 --- a/wlauto/common/android/workload.py +++ b/wlauto/common/android/workload.py @@ -301,19 +301,24 @@ class ReventWorkload(Workload): default_setup_timeout = 5 * 60 # in seconds default_run_timeout = 10 * 60 # in seconds + @property + def on_device_setup_revent(self): + return self.device.get_workpath('{}.setup.revent'.format(self.device.model)) + + @property + def on_device_run_revent(self): + return self.device.get_workpath('{}.run.revent'.format(self.device.model)) + def __init__(self, device, _call_super=True, **kwargs): if _call_super: super(ReventWorkload, self).__init__(device, **kwargs) - devpath = self.device.path - self.on_device_revent_binary = devpath.join(self.device.binaries_directory, 'revent') - self.on_device_setup_revent = devpath.join(self.device.working_directory, '{}.setup.revent'.format(self.device.name)) - self.on_device_run_revent = devpath.join(self.device.working_directory, '{}.run.revent'.format(self.device.name)) + self.on_device_revent_binary = None self.setup_timeout = kwargs.get('setup_timeout', self.default_setup_timeout) self.run_timeout = kwargs.get('run_timeout', self.default_run_timeout) self.revent_setup_file = None self.revent_run_file = None - def init_resources(self, context): + def initialize(self, context): self.revent_setup_file = context.resolver.get(wlauto.common.android.resources.ReventFile(self, 'setup')) self.revent_run_file = context.resolver.get(wlauto.common.android.resources.ReventFile(self, 'run')) @@ -433,8 +438,11 @@ class GameWorkload(ApkWorkload, ReventWorkload): self.module_dir = os.path.dirname(sys.modules[self.__module__].__file__) self.revent_dir = os.path.join(self.module_dir, 'revent_files') - def init_resources(self, context): + def apk_init_resources(self, context): ApkWorkload.init_resources(self, context) + + def init_resources(self, context): + self.apk_init_resources(context) ReventWorkload.init_resources(self, context) def setup(self, context): @@ -461,11 +469,14 @@ class GameWorkload(ApkWorkload, ReventWorkload): def run(self, context): ReventWorkload.run(self, context) - def teardown(self, context): + def apk_teardown(self, context): if not self.saved_state_file: ApkWorkload.teardown(self, context) else: self.device.execute('am force-stop {}'.format(self.package)) + + def teardown(self, context): + self.apk_teardown(context) ReventWorkload.teardown(self, context) def _deploy_assets(self, context, timeout=300): @@ -478,7 +489,7 @@ class GameWorkload(ApkWorkload, ReventWorkload): kind = 'data' if ':' in resource_file: kind, resource_file = resource_file.split(':', 1) - ondevice_cache = self.device.path.join(self.device.resource_cache, self.name, resource_file) + ondevice_cache = self.device.path.join(self.device.working_directory, '.cache', self.name, resource_file) if not self.device.file_exists(ondevice_cache): asset_tarball = context.resolver.get(PluginAsset(self, resource_file)) if not asset_tarball: @@ -488,7 +499,7 @@ class GameWorkload(ApkWorkload, ReventWorkload): # exist. self.device.push(asset_tarball, ondevice_cache, timeout=timeout) - device_asset_directory = self.device.path.join(self.context.device_manager.external_storage_directory, 'Android', kind) + device_asset_directory = self.device.path.join(context.device_manager.external_storage_directory, 'Android', kind) deploy_command = 'cd {} && {} tar -xzf {}'.format(device_asset_directory, self.device.busybox, ondevice_cache)