1
0
mirror of https://github.com/ARM-software/workload-automation.git synced 2025-02-20 20:09:11 +00:00

Merge pull request #381 from marcbonnici/revent

Revent & Misc Fixes
This commit is contained in:
setrofim 2017-04-25 10:39:41 +01:00 committed by GitHub
commit 63b01e29ee
17 changed files with 818 additions and 108 deletions

View File

@ -14,4 +14,4 @@ from wa.framework.plugin import Plugin, Parameter
from wa.framework.processor import ResultProcessor
from wa.framework.resource import (NO_ONE, JarFile, ApkFile, ReventFile, File,
Executable)
from wa.framework.workload import Workload, ApkUiautoWorkload
from wa.framework.workload import Workload, ApkUiautoWorkload, ReventWorkload

View File

@ -15,21 +15,24 @@
import os
import sys
from time import sleep
from wa import Command, settings
from wa.framework.configuration import RunConfiguration
from wa.framework.resource import Executable, NO_ONE, ResourceResolver
from wa import Command
from wa.framework import pluginloader
from wa.framework.resource import ResourceResolver
from wa.framework.target.manager import TargetManager
from wa.utils.revent import ReventRecorder
class RecordCommand(Command):
name = 'record'
description = '''Performs a revent recording
description = '''
Performs a revent recording
This command helps making revent recordings. It will automatically
deploy revent and even has the option of automatically opening apps.
deploy revent and has options to automatically open apps and record
specified stages of a workload.
Revent allows you to record raw inputs such as screen swipes or button presses.
This can be useful for recording inputs for workloads such as games that don't
@ -43,10 +46,18 @@ class RecordCommand(Command):
it can be automatically determined. On Android device it will be obtained
from ``build.prop``, on Linux devices it is obtained from ``/proc/device-tree/model``.
- suffix is used by WA to determine which part of the app execution the
recording is for, currently these are either ``setup`` or ``run``. This
should be specified with the ``-s`` argument.
recording is for, currently these are either ``setup``, ``run``, ``extract_results``
or ``teardown``. All stages except ``run`` are optional and these should
be specified with the ``-s``, ``-e`` or ``-t`` arguments respectively,
or optionally ``-a`` to indicate all stages should be recorded.
'''
def __init__(self, **kwargs):
super(RecordCommand, self).__init__(**kwargs)
self.tm = None
self.target = None
self.revent_recorder = None
def initialize(self, context):
self.parser.add_argument('-d', '--device', metavar='DEVICE',
help='''
@ -54,39 +65,204 @@ class RecordCommand(Command):
take precedence over the device (if any)
specified in configuration.
''')
self.parser.add_argument('-o', '--output', help='Specify the output file', metavar='FILE')
self.parser.add_argument('-s', '--setup', help='Record a recording for setup stage',
action='store_true')
self.parser.add_argument('-e', '--extract_results', help='Record a recording for extract_results stage',
action='store_true')
self.parser.add_argument('-t', '--teardown', help='Record a recording for teardown stage',
action='store_true')
self.parser.add_argument('-a', '--all', help='Record recordings for available stages',
action='store_true')
def execute(state, args):
# Need validation
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)')
def validate_args(self, args):
if args.clear and not (args.package or args.workload):
self.logger.error("Package/Workload must be specified if you want to clear cache")
sys.exit()
if args.workload and args.output:
self.logger.error("Output file cannot be specified with Workload")
sys.exit()
if not args.workload and (args.setup or args.extract_results or
args.teardown or args.all):
self.logger.error("Cannot specify a recording stage without a Workload")
sys.exit()
def execute(self, state, args):
self.validate_args(args)
if args.device:
device = args.device
device = args.device
device_config = {}
else:
device = state.run_config.device
device_config = state.run_config.device_config
device_config = state.run_config.device_config or {}
self.tm = TargetManager(device, device_config)
self.target = self.tm.target
self.revent_recorder = ReventRecorder(self.target)
self.revent_recorder.deploy()
if args.workload:
self.workload_record(args)
elif args.package:
self.package_record(args)
else:
self.manual_record(args)
self.revent_recorder.remove()
def record(self, revent_file, name, output_path):
msg = 'Press Enter when you are ready to record {}...'
self.logger.info(msg.format(name))
raw_input('')
self.revent_recorder.start_record(revent_file)
msg = 'Press Enter when you have finished recording {}...'
self.logger.info(msg.format(name))
raw_input('')
self.revent_recorder.stop_record()
if not os.path.isdir(output_path):
os.makedirs(output_path)
revent_file_name = self.target.path.basename(revent_file)
host_path = os.path.join(output_path, revent_file_name)
if os.path.exists(host_path):
msg = 'Revent file \'{}\' already exists, overwrite? [y/n]'
self.logger.info(msg.format(revent_file_name))
if raw_input('') == 'y':
os.remove(host_path)
else:
msg = 'Did not pull and overwrite \'{}\''
self.logger.warning(msg.format(revent_file_name))
return
msg = 'Pulling \'{}\' from device'
self.logger.info(msg.format(self.target.path.basename(revent_file)))
self.target.pull(revent_file, output_path, as_root=self.target.is_rooted)
def manual_record(self, args):
output_path, file_name = self._split_revent_location(args.output)
revent_file = self.target.get_workpath(file_name)
self.record(revent_file, '', output_path)
msg = 'Recording is available at: \'{}\''
self.logger.info(msg.format(os.path.join(output_path, file_name)))
def package_record(self, args):
if args.clear:
self.target.execute('pm clear {}'.format(args.package))
self.logger.info('Starting {}'.format(args.package))
cmd = 'monkey -p {} -c android.intent.category.LAUNCHER 1'
self.target.execute(cmd.format(args.package))
output_path, file_name = self._split_revent_location(args.output)
revent_file = self.target.get_workpath(file_name)
self.record(revent_file, '', output_path)
msg = 'Recording is available at: \'{}\''
self.logger.info(msg.format(os.path.join(output_path, file_name)))
def workload_record(self, args):
context = LightContext(self.tm)
setup_revent = '{}.setup.revent'.format(self.target.model)
run_revent = '{}.run.revent'.format(self.target.model)
extract_results_revent = '{}.extract_results.revent'.format(self.target.model)
teardown_file_revent = '{}.teardown.revent'.format(self.target.model)
setup_file = self.target.get_workpath(setup_revent)
run_file = self.target.get_workpath(run_revent)
extract_results_file = self.target.get_workpath(extract_results_revent)
teardown_file = self.target.get_workpath(teardown_file_revent)
self.logger.info('Deploying {}'.format(args.workload))
workload = pluginloader.get_workload(args.workload, self.target)
workload.apk.init_resources(context.resolver)
workload.apk.setup(context)
sleep(workload.loading_time)
output_path = os.path.join(workload.dependencies_directory,
'revent_files')
if args.setup or args.all:
self.record(setup_file, 'SETUP', output_path)
self.record(run_file, 'RUN', output_path)
if args.extract_results or args.all:
self.record(extract_results_file, 'EXTRACT_RESULTS', output_path)
if args.teardown or args.all:
self.record(teardown_file, 'TEARDOWN', output_path)
self.logger.info('Tearing down {}'.format(args.workload))
workload.teardown(context)
self.logger.info('Recording(s) are available at: \'{}\''.format(output_path))
def _split_revent_location(self, output):
output_path = None
file_name = None
if output:
output_path, file_name, = os.path.split(output)
if not file_name:
file_name = '{}.revent'.format(self.target.model)
if not output_path:
output_path = os.getcwdu()
return output_path, file_name
class ReplayCommand(Command):
name = 'replay'
description = '''
Replay a revent recording
Revent allows you to record raw inputs such as screen swipes or button presses.
See ``wa show record`` to see how to make an revent recording.
'''
def initialize(self, context):
self.parser.add_argument('recording', help='The name of the file to replay',
metavar='FILE')
self.parser.add_argument('-d', '--device', help='The name of the device')
self.parser.add_argument('-p', '--package', help='Package to launch before recording')
self.parser.add_argument('-C', '--clear', help='Clear app cache before launching it',
action="store_true")
# pylint: disable=W0201
def execute(self, state, args):
if args.device:
device = args.device
device_config = {}
else:
device = state.run_config.device
device_config = state.run_config.device_config or {}
target_manager = TargetManager(device, device_config)
self.target = target_manager.target
revent_file = self.target.path.join(self.target.working_directory,
os.path.split(args.revent)[1])
self.logger.info("Pushing file to target")
self.target.push(args.revent, self.target.working_directory)
revent_recorder = ReventRecorder(target_manager.target)
revent_recorder.deploy()
if args.clear:
self.target.execute('pm clear {}'.format(args.package))
if args.package:
self.logger.info('Starting {}'.format(args.package))
cmd = 'monkey -p {} -c android.intent.category.LAUNCHER 1'
self.target.execute(cmd.format(args.package))
self.logger.info("Starting replay")
revent_recorder.replay(revent_file)
self.logger.info("Finished replay")
revent_recorder.remove()
def get_revent_binary(abi):
resolver = ResourceResolver()
resource = Executable(NO_ONE, abi, 'revent')
return resolver.get(resource)
class ReventRecorder(object):
def __init__(self, target):
self.target = target
self.executable = None
self.deploy()
def deploy(self):
host_executable = get_revent_binary(self.target.abi)
self.executable = self.target.install(host_executable)
def record(self, path):
name = os.path.basename(path)
target_path = self.target.get_workpath(name)
command = '{} record {}'
def remove(self):
if self.executable:
self.target.uninstall('revent')
# Used to satisfy the workload API
class LightContext(object):
def __init__(self, tm):
self.tm = tm
self.resolver = ResourceResolver()
self.resolver.load()

View File

@ -12,8 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from wlauto.core.configuration.configuration import (settings,
RunConfiguration,
JobGenerator,
ConfigurationPoint)
from wlauto.core.configuration.plugin_cache import PluginCache
from wa.framework.configuration.core import (settings,
RunConfiguration,
JobGenerator,
ConfigurationPoint)

View File

@ -1,7 +1,7 @@
from wlauto.core.configuration.configuration import MetaConfiguration, RunConfiguration
from wlauto.core.configuration.plugin_cache import PluginCache
from wlauto.utils.serializer import yaml
from wlauto.utils.doc import strip_inlined_text
from wa.framework.configuration.core import MetaConfiguration, RunConfiguration
from wa.framework.configuration.plugin_cache import PluginCache
from wa.utils.serializer import yaml
from wa.utils.doc import strip_inlined_text
DEFAULT_INSTRUMENTS = ['execution_time',
'interrupts',

View File

@ -209,7 +209,7 @@ class Executor(object):
"""
The ``Executor``'s job is to set up the execution context and pass to a
``Runner`` along with a loaded run specification. Once the ``Runner`` has
done its thing, the ``Executor`` performs some final reporint before
done its thing, the ``Executor`` performs some final reporting before
returning.
The initial context set up involves combining configuration from various
@ -225,7 +225,7 @@ class Executor(object):
self.error_logged = False
self.warning_logged = False
pluginloader = None
self.device_manager = None
self.target_manager = None
self.device = None
def execute(self, config_manager, output):
@ -249,12 +249,12 @@ class Executor(object):
output.write_config(config)
self.logger.info('Connecting to target')
target_manager = TargetManager(config.run_config.device,
self.target_manager = TargetManager(config.run_config.device,
config.run_config.device_config)
output.write_target_info(target_manager.get_target_info())
output.write_target_info(self.target_manager.get_target_info())
self.logger.info('Initializing execution conetext')
context = ExecutionContext(config_manager, target_manager, output)
context = ExecutionContext(config_manager, self.target_manager, output)
self.logger.info('Generating jobs')
config_manager.generate_jobs(context)
@ -262,7 +262,7 @@ class Executor(object):
output.write_state()
self.logger.info('Installing instrumentation')
for instrument in config_manager.get_instruments(target_manager.target):
for instrument in config_manager.get_instruments(self.target_manager.target):
instrumentation.install(instrument)
instrumentation.validate()
@ -361,6 +361,10 @@ class Runner(object):
self.pm.process_run_output(self.context)
self.pm.export_run_output(self.context)
self.pm.finalize()
log.indent()
for job in self.context.completed_jobs:
job.finalize(self.context)
log.dedent()
def run_next_job(self, context):
job = context.start_job()

View File

@ -31,7 +31,7 @@ import requests
from devlib.utils.android import ApkInfo
from wa import Parameter, settings, __file__ as __base_filepath
from wa import Parameter, settings, __file__ as _base_filepath
from wa.framework.resource import ResourceGetter, SourcePriority, NO_ONE
from wa.framework.exception import ResourceError
from wa.utils.misc import (ensure_directory_exists as _d,
@ -80,7 +80,16 @@ def get_from_location(basepath, resource):
path = os.path.join(basepath, 'bin', resource.abi, resource.filename)
if os.path.exists(path):
return path
elif resource.kind in ['apk', 'jar', 'revent']:
elif resource.kind == 'revent':
path = os.path.join(basepath, 'revent_files')
if os.path.exists(path):
files = get_by_extension(path, resource.kind)
found_resource = get_generic_resource(resource, files)
if found_resource:
return found_resource
files = get_by_extension(basepath, resource.kind)
return get_generic_resource(resource, files)
elif resource.kind in ['apk', 'jar']:
files = get_by_extension(basepath, resource.kind)
return get_generic_resource(resource, files)
@ -96,7 +105,7 @@ class Package(ResourceGetter):
def get(self, resource):
if resource.owner == NO_ONE:
basepath = os.path.join(os.path.dirname(__base_filepath), 'assets')
basepath = os.path.join(os.path.dirname(_base_filepath), 'assets')
else:
modname = resource.owner.__module__
basepath = os.path.dirname(sys.modules[modname].__file__)

View File

@ -1,6 +1,6 @@
import os
from wlauto.core.configuration import settings
from wa.framework.configuration.core import settings
def init_user_directory(overwrite_existing=False): # pylint: disable=R0914
"""

View File

@ -26,7 +26,7 @@ from itertools import chain
from copy import copy
from wa.framework.configuration.core import settings, ConfigurationPoint as Parameter
from wa.framework.exception import (NotFoundError, PluginLoaderError,
from wa.framework.exception import (NotFoundError, PluginLoaderError, TargetError,
ValidationError, ConfigError, HostError)
from wa.utils import log
from wa.utils.misc import (ensure_directory_exists as _d, walk_modules, load_class,
@ -430,7 +430,7 @@ class Plugin(object):
get_module(name, owner, **kwargs)
and returns an instance of :class:`wlauto.core.plugin.Module`. If the
and returns an instance of :class:`wa.core.plugin.Module`. If the
module with the specified name is not found, the loader must raise an
appropriate exception.
@ -743,10 +743,10 @@ class PluginLoader(object):
self.logger.warning('Got: {}'.format(e))
else:
msg = 'Failed to load {}'
raise LoaderError(msg.format(filepath), sys.exc_info())
raise PluginLoaderError(msg.format(filepath), sys.exc_info())
except Exception as e:
message = 'Problem loading plugins from {}: {}'
raise LoaderError(message.format(filepath, e))
raise PluginLoaderError(message.format(filepath, e))
def _discover_in_module(self, module): # NOQA pylint: disable=too-many-branches
self.logger.debug('Checking module %s', module.__name__)

View File

@ -231,7 +231,7 @@ def connect(handler, signal, sender=dispatcher.Any, priority=0):
.. note:: There is nothing that prevents instrumentation from sending their
own signals that are not part of the standard set. However the signal
must always be an :class:`wlauto.core.signal.Signal` instance.
must always be an :class:`wa.core.signal.Signal` instance.
:sender: The handler will be invoked only for the signals emitted by this sender. By
default, this is set to :class:`louie.dispatcher.Any`, so the handler will
@ -270,7 +270,7 @@ def disconnect(handler, signal, sender=dispatcher.Any):
:handler: The callback to be disconnected.
:signal: The signal the handler is to be disconnected form. It will
be an :class:`wlauto.core.signal.Signal` instance.
be an :class:`wa.core.signal.Signal` instance.
:sender: If specified, the handler will only be disconnected from the signal
sent by this sender.
@ -284,7 +284,7 @@ def send(signal, sender=dispatcher.Anonymous, *args, **kwargs):
Paramters:
:signal: Signal to be sent. This must be an instance of :class:`wlauto.core.signal.Signal`
:signal: Signal to be sent. This must be an instance of :class:`wa.core.signal.Signal`
or its subclasses.
:sender: The sender of the signal (typically, this would be ``self``). Some handlers may only
be subscribed to signals from a particular sender.

View File

@ -33,6 +33,11 @@ def instantiate_target(tdesc, params, connect=None):
tp, pp, cp = {}, {}, {}
for supported_params, new_params in (target_params, tp), (platform_params, pp), (conn_params, cp):
for name, value in supported_params.iteritems():
if value.default:
new_params[name] = value.default
for name, value in params.iteritems():
if name in target_params:
tp[name] = value
@ -62,6 +67,8 @@ def instantiate_assistant(tdesc, params, target):
for param in tdesc.assistant_params:
if param.name in params:
assistant_params[param.name] = params[param.name]
elif param.default:
assistant_params[param.name] = param.default
return tdesc.assistant(target, **assistant_params)

View File

@ -17,8 +17,11 @@ import os
import time
from wa.framework.plugin import TargetedPlugin
from wa.framework.resource import ApkFile, JarFile, ReventFile, NO_ONE
from wa.framework.resource import (ApkFile, JarFile, ReventFile, NO_ONE,
Executable, File)
from wa.framework.exception import WorkloadError
from wa.utils.revent import ReventRecorder
from wa.utils.exec_control import once
from devlib.utils.android import ApkInfo
@ -64,7 +67,7 @@ class Workload(TargetedPlugin):
def run(self, context):
"""
Execute the workload. This is the method that performs the actual
"work" of the.
"work" of the workload.
"""
pass
@ -93,25 +96,29 @@ class Workload(TargetedPlugin):
return '<Workload {}>'.format(self.name)
class ApkUiautoWorkload(Workload):
platform = 'android'
class ApkUIWorkload(Workload):
# May be optionally overwritten by subclasses
# Times are in seconds
loading_time = 10
def __init__(self, target, **kwargs):
super(ApkUiautoWorkload, self).__init__(target, **kwargs)
self.apk = ApkHander(self)
self.gui = UiAutomatorGUI(self)
super(ApkUIWorkload, self).__init__(target, **kwargs)
self.apk = None
self.gui = None
def init_resources(self, context):
self.apk.init_resources(context.resolver)
self.gui.init_resources(context.resolver)
self.gui.init_commands()
@once
def initialize(self, context):
self.gui.deploy()
def setup(self, context):
self.apk.setup(context)
time.sleep(self.loading_time)
self.gui.setup()
def run(self, context):
@ -124,10 +131,40 @@ class ApkUiautoWorkload(Workload):
self.gui.teardown()
self.apk.teardown()
@once
def finalize(self, context):
self.gui.remove()
class ApkUiautoWorkload(ApkUIWorkload):
platform = 'android'
def __init__(self, target, **kwargs):
super(ApkUiautoWorkload, self).__init__(target, **kwargs)
self.apk = ApkHander(self)
self.gui = UiAutomatorGUI(self)
class ReventWorkload(ApkUIWorkload):
# May be optionally overwritten by subclasses
# Times are in seconds
setup_timeout = 5 * 60
run_timeout = 10 * 60
extract_results_timeout = 5 * 60
teardown_timeout = 5 * 60
def __init__(self, target, **kwargs):
super(ReventWorkload, self).__init__(target, **kwargs)
self.apk = ApkHander(self)
self.gui = ReventGUI(self, target,
self.setup_timeout,
self.run_timeout,
self.extract_results_timeout,
self.teardown_timeout)
class UiAutomatorGUI(object):
stages = ['setup', 'runWorkload', 'extractResults', 'teardown']
@ -208,57 +245,92 @@ class UiAutomatorGUI(object):
class ReventGUI(object):
def __init__(self, workload, target, setup_timeout=5 * 60, run_timeout=10 * 60):
def __init__(self, workload, target, setup_timeout, run_timeout,
extract_results_timeout, teardown_timeout):
self.workload = workload
self.target = target
self.setup_timeout = setup_timeout
self.run_timeout = run_timeout
self.extract_results_timeout = extract_results_timeout
self.teardown_timeout = teardown_timeout
self.revent_recorder = ReventRecorder(self.target)
self.on_target_revent_binary = self.target.get_workpath('revent')
self.on_target_setup_revent = self.target.get_workpath('{}.setup.revent'.format(self.target.name))
self.on_target_run_revent = self.target.get_workpath('{}.run.revent'.format(self.target.name))
self.on_target_setup_revent = self.target.get_workpath('{}.setup.revent'.format(self.target.model))
self.on_target_run_revent = self.target.get_workpath('{}.run.revent'.format(self.target.model))
self.on_target_extract_results_revent = self.target.get_workpath('{}.extract_results.revent'.format(self.target.model))
self.on_target_teardown_revent = self.target.get_workpath('{}.teardown.revent'.format(self.target.model))
self.logger = logging.getLogger('revent')
self.revent_setup_file = None
self.revent_run_file = None
self.revent_extract_results_file = None
self.revent_teardown_file = None
def init_resources(self, context):
self.revent_setup_file = context.resolver.get(ReventFile(self.workload, 'setup'))
self.revent_run_file = context.resolver.get(ReventFile(self.workload, 'run'))
def init_resources(self, resolver):
self.revent_setup_file = resolver.get(ReventFile(owner=self.workload,
stage='setup',
target=self.target.model),
strict=False)
self.revent_run_file = resolver.get(ReventFile(owner=self.workload,
stage='run',
target=self.target.model))
self.revent_extract_results_file = resolver.get(ReventFile(owner=self.workload,
stage='extract_results',
target=self.target.model),
strict=False)
self.revent_teardown_file = resolver.get(resource=ReventFile(owner=self.workload,
stage='teardown',
target=self.target.model),
strict=False)
def setup(self, context):
self._check_revent_files(context)
self.target.killall('revent')
command = '{} replay {}'.format(self.on_target_revent_binary, self.on_target_setup_revent)
self.target.execute(command, timeout=self.setup_timeout)
def deploy(self):
self.revent_recorder.deploy()
def run(self, context):
command = '{} replay {}'.format(self.on_target_revent_binary, self.on_target_run_revent)
self.logger.debug('Replaying {}'.format(os.path.basename(self.on_target_run_revent)))
self.target.execute(command, timeout=self.run_timeout)
def setup(self):
self._check_revent_files()
self.revent_recorder.replay(self.on_target_setup_revent,
timeout=self.setup_timeout)
def run(self):
msg = 'Replaying {}'
self.logger.debug(msg.format(os.path.basename(self.on_target_run_revent)))
self.revent_recorder.replay(self.on_target_run_revent,
timeout=self.run_timeout)
self.logger.debug('Replay completed.')
def teardown(self, context):
def extract_results(self):
if self.revent_extract_results_file:
self.revent_recorder.replay(self.on_target_extract_results_revent,
timeout=self.extract_results_timeout)
def teardown(self):
if self.revent_teardown_file:
self.revent_recorder.replay(self.on_target_teardown_revent,
timeout=self.teardown_timeout)
self.target.remove(self.on_target_setup_revent)
self.target.remove(self.on_target_run_revent)
self.target.remove(self.on_target_extract_results_revent)
self.target.remove(self.on_target_teardown_revent)
def _check_revent_files(self, context):
# check the revent binary
revent_binary = context.resolver.get(Executable(NO_ONE, self.target.abi, 'revent'))
if not os.path.isfile(revent_binary):
message = '{} does not exist. '.format(revent_binary)
message += 'Please build revent for your system and place it in that location'
raise WorkloadError(message)
if not self.revent_setup_file:
# pylint: disable=too-few-format-args
message = '{0}.setup.revent file does not exist, Please provide one for your target, {0}'
raise WorkloadError(message.format(self.target.name))
def remove(self):
self.revent_recorder.remove()
def init_commands(self):
pass
def _check_revent_files(self):
if not self.revent_run_file:
# pylint: disable=too-few-format-args
message = '{0}.run.revent file does not exist, Please provide one for your target, {0}'
raise WorkloadError(message.format(self.target.name))
message = '{0}.run.revent file does not exist, ' \
'Please provide one for your target, {0}'
raise WorkloadError(message.format(self.target.model))
self.on_target_revent_binary = self.target.install(revent_binary)
self.target.push(self.revent_run_file, self.on_target_run_revent)
self.target.push(self.revent_setup_file, self.on_target_setup_revent)
if self.revent_setup_file:
self.target.push(self.revent_setup_file, self.on_target_setup_revent)
if self.revent_extract_results_file:
self.target.push(self.revent_extract_results_file, self.on_target_extract_results_revent)
if self.revent_teardown_file:
self.target.push(self.revent_teardown_file, self.on_target_teardown_revent)
class ApkHander(object):
@ -280,10 +352,10 @@ class ApkHander(object):
self.logcat_log = None
def init_resources(self, resolver):
self.apk_file = resolver.get(ApkFile(self.owner,
self.apk_file = resolver.get(ApkFile(self.owner,
variant=self.variant,
version=self.version),
strict=self.strict)
version=self.version),
strict=self.strict)
self.apk_info = ApkInfo(self.apk_file)
def setup(self, context):
@ -332,7 +404,7 @@ class ApkHander(object):
def start_activity(self):
cmd = 'am start -W -n {}/{}'
output = self.target.execute(cmd.format(self.apk_info.package,
output = self.target.execute(cmd.format(self.apk_info.package,
self.apk_info.activity))
if 'Error:' in output:
# this will dismiss any error dialogs

View File

@ -22,7 +22,7 @@ from unittest import TestCase
from nose.tools import assert_equal
from wlauto.instrumentation.misc import _diff_interrupt_files
from wa.instrumentation.misc import _diff_interrupt_files
class InterruptDiffTest(TestCase):

View File

@ -0,0 +1,269 @@
# Copyright 2013-2015 ARM Limited
#
# 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.
#
# pylint: disable=W0231,W0613,E0611,W0603,R0201
from unittest import TestCase
from nose.tools import assert_equal, assert_raises
from wlauto.utils.exec_control import (init_environment, reset_environment,
activate_environment, once,
once_per_class, once_per_instance)
class TestClass(object):
def __init__(self):
self.count = 0
@once
def initilize_once(self):
self.count += 1
@once_per_class
def initilize_once_per_class(self):
self.count += 1
@once_per_instance
def initilize_once_per_instance(self):
self.count += 1
class SubClass(TestClass):
def __init__(self):
super(SubClass, self).__init__()
class SubSubClass(SubClass):
def __init__(self):
super(SubSubClass, self).__init__()
class AnotherClass(object):
def __init__(self):
self.count = 0
@once
def initilize_once(self):
self.count += 1
@once_per_class
def initilize_once_per_class(self):
self.count += 1
@once_per_instance
def initilize_once_per_instance(self):
self.count += 1
class EnvironmentManagementTest(TestCase):
def test_duplicate_environment(self):
init_environment('ENVIRONMENT')
assert_raises(ValueError, init_environment, 'ENVIRONMENT')
def test_reset_missing_environment(self):
assert_raises(ValueError, reset_environment, 'MISSING')
def test_reset_current_environment(self):
activate_environment('CURRENT_ENVIRONMENT')
t1 = TestClass()
t1.initilize_once()
assert_equal(t1.count, 1)
reset_environment()
t1.initilize_once()
assert_equal(t1.count, 2)
def test_switch_environment(self):
activate_environment('ENVIRONMENT1')
t1 = TestClass()
t1.initilize_once()
assert_equal(t1.count, 1)
activate_environment('ENVIRONMENT2')
t1.initilize_once()
assert_equal(t1.count, 2)
activate_environment('ENVIRONMENT1')
t1.initilize_once()
assert_equal(t1.count, 2)
def test_reset_environment_name(self):
activate_environment('ENVIRONMENT')
t1 = TestClass()
t1.initilize_once()
assert_equal(t1.count, 1)
reset_environment('ENVIRONMENT')
t1.initilize_once()
assert_equal(t1.count, 2)
class OnlyOnceEnvironmentTest(TestCase):
def setUp(self):
activate_environment('TEST_ENVIRONMENT')
def tearDown(self):
reset_environment('TEST_ENVIRONMENT')
def test_single_instance(self):
t1 = TestClass()
ac = AnotherClass()
t1.initilize_once()
assert_equal(t1.count, 1)
t1.initilize_once()
assert_equal(t1.count, 1)
ac.initilize_once()
assert_equal(ac.count, 1)
def test_mulitple_instances(self):
t1 = TestClass()
t2 = TestClass()
t1.initilize_once()
assert_equal(t1.count, 1)
t2.initilize_once()
assert_equal(t2.count, 0)
def test_sub_classes(self):
t1 = TestClass()
sc = SubClass()
ss = SubSubClass()
t1.initilize_once()
assert_equal(t1.count, 1)
sc.initilize_once()
sc.initilize_once()
assert_equal(sc.count, 0)
ss.initilize_once()
ss.initilize_once()
assert_equal(ss.count, 0)
class OncePerClassEnvironmentTest(TestCase):
def setUp(self):
activate_environment('TEST_ENVIRONMENT')
def tearDown(self):
reset_environment('TEST_ENVIRONMENT')
def test_single_instance(self):
t1 = TestClass()
ac = AnotherClass()
t1.initilize_once_per_class()
assert_equal(t1.count, 1)
t1.initilize_once_per_class()
assert_equal(t1.count, 1)
ac.initilize_once_per_class()
assert_equal(ac.count, 1)
def test_mulitple_instances(self):
t1 = TestClass()
t2 = TestClass()
t1.initilize_once_per_class()
assert_equal(t1.count, 1)
t2.initilize_once_per_class()
assert_equal(t2.count, 0)
def test_sub_classes(self):
t1 = TestClass()
sc1 = SubClass()
sc2 = SubClass()
ss1 = SubSubClass()
ss2 = SubSubClass()
t1.initilize_once_per_class()
assert_equal(t1.count, 1)
sc1.initilize_once_per_class()
sc2.initilize_once_per_class()
assert_equal(sc1.count, 1)
assert_equal(sc2.count, 0)
ss1.initilize_once_per_class()
ss2.initilize_once_per_class()
assert_equal(ss1.count, 1)
assert_equal(ss2.count, 0)
class OncePerInstanceEnvironmentTest(TestCase):
def setUp(self):
activate_environment('TEST_ENVIRONMENT')
def tearDown(self):
reset_environment('TEST_ENVIRONMENT')
def test_single_instance(self):
t1 = TestClass()
ac = AnotherClass()
t1.initilize_once_per_instance()
assert_equal(t1.count, 1)
t1.initilize_once_per_instance()
assert_equal(t1.count, 1)
ac.initilize_once_per_instance()
assert_equal(ac.count, 1)
def test_mulitple_instances(self):
t1 = TestClass()
t2 = TestClass()
t1.initilize_once_per_instance()
assert_equal(t1.count, 1)
t2.initilize_once_per_instance()
assert_equal(t2.count, 1)
def test_sub_classes(self):
t1 = TestClass()
sc = SubClass()
ss = SubSubClass()
t1.initilize_once_per_instance()
assert_equal(t1.count, 1)
sc.initilize_once_per_instance()
sc.initilize_once_per_instance()
assert_equal(sc.count, 1)
ss.initilize_once_per_instance()
ss.initilize_once_per_instance()
assert_equal(ss.count, 1)

110
wa/utils/exec_control.py Normal file
View File

@ -0,0 +1,110 @@
from inspect import getmro
# "environment" management:
__environments = {}
__active_environment = None
def activate_environment(name):
"""
Sets the current tracking environment to ``name``. If an
environment with that name does not already exist, it will be
created.
"""
#pylint: disable=W0603
global __active_environment
if name not in __environments.keys():
init_environment(name)
__active_environment = name
def init_environment(name):
"""
Create a new environment called ``name``, but do not set it as the
current environment.
:raises: ``ValueError`` if an environment with name ``name``
already exists.
"""
if name in __environments.keys():
msg = "Environment {} already exists".format(name)
raise ValueError(msg)
__environments[name] = []
def reset_environment(name=None):
"""
Reset method call tracking for environment ``name``. If ``name`` is
not specified or is ``None``, reset the current active environment.
:raises: ``ValueError`` if an environment with name ``name``
does not exist.
"""
if name is not None:
if name not in __environments.keys():
msg = "Environment {} does not exist".format(name)
raise ValueError(msg)
__environments[name] = []
else:
if __active_environment is None:
activate_environment('default')
__environments[__active_environment] = []
# The decorators:
def once_per_instance(method):
"""
The specified method will be invoked only once for every bound
instance within the environment.
"""
def wrapper(*args, **kwargs):
if __active_environment is None:
activate_environment('default')
func_id = repr(args[0])
if func_id in __environments[__active_environment]:
return
else:
__environments[__active_environment].append(func_id)
return method(*args, **kwargs)
return wrapper
def once_per_class(method):
"""
The specified method will be invoked only once for all instances
of a class within the environment.
"""
def wrapper(*args, **kwargs):
if __active_environment is None:
activate_environment('default')
func_id = repr(method.func_name) + repr(args[0].__class__)
if func_id in __environments[__active_environment]:
return
else:
__environments[__active_environment].append(func_id)
return method(*args, **kwargs)
return wrapper
def once(method):
"""
The specified method will be invoked only once within the
environment.
"""
def wrapper(*args, **kwargs):
if __active_environment is None:
activate_environment('default')
func_id = repr(method.func_name)
# Store the least derived class, which isn't object, to account
# for subclasses.
func_id += repr(getmro(args[0].__class__)[-2])
if func_id in __environments[__active_environment]:
return
else:
__environments[__active_environment].append(func_id)
return method(*args, **kwargs)
return wrapper

View File

@ -54,7 +54,7 @@ from devlib.utils.misc import (ABI_MAP, check_output, walk_modules,
check_output_logger = logging.getLogger('check_output')
# Defined here rather than in wlauto.exceptions due to module load dependencies
# Defined here rather than in wa.exceptions due to module load dependencies
def diff_tokens(before_token, after_token):
"""
Creates a diff of two tokens.

View File

@ -16,9 +16,11 @@
from __future__ import division
import os
import struct
import signal
from datetime import datetime
from collections import namedtuple
from wa.framework.resource import Executable, NO_ONE, ResourceResolver
GENERAL_MODE = 0
GAMEPAD_MODE = 1
@ -249,3 +251,38 @@ class ReventRecording(object):
def __del__(self):
self.close()
def get_revent_binary(abi):
resolver = ResourceResolver()
resolver.load()
resource = Executable(NO_ONE, abi, 'revent')
return resolver.get(resource)
class ReventRecorder(object):
def __init__(self, target):
self.target = target
self.executable = self.target.get_installed('revent')
def deploy(self):
if not self.executable:
host_executable = get_revent_binary(self.target.abi)
self.executable = self.target.install(host_executable)
def remove(self):
if self.executable:
self.target.uninstall('revent')
def start_record(self, revent_file):
command = '{} record -s {}'.format(self.executable, revent_file)
self.target.kick_off(command, self.target.is_rooted)
def stop_record(self):
self.target.killall('revent', signal.SIGINT, as_root=self.target.is_rooted)
def replay(self, revent_file, timeout=None):
self.target.killall('revent')
command = "{} replay {}".format(self.executable, revent_file)
self.target.execute(command, timeout=timeout)

View File

@ -0,0 +1,27 @@
# Copyright 2013-2015 ARM Limited
#
# 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.
#
from wa import ReventWorkload
class AngryBirdsRio(ReventWorkload):
name = 'angrybirds_rio'
description = """
Angry Birds Rio game.
The sequel to the very popular Android 2D game.
"""