From 0057a531fdb0e0d069970f0bc991ef4dd01efe3b Mon Sep 17 00:00:00 2001
From: Sebastian Goscik <sebastian.goscik@live.co.uk>
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)