mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-02-21 20:38:57 +00:00
commit
63b01e29ee
@ -14,4 +14,4 @@ from wa.framework.plugin import Plugin, Parameter
|
|||||||
from wa.framework.processor import ResultProcessor
|
from wa.framework.processor import ResultProcessor
|
||||||
from wa.framework.resource import (NO_ONE, JarFile, ApkFile, ReventFile, File,
|
from wa.framework.resource import (NO_ONE, JarFile, ApkFile, ReventFile, File,
|
||||||
Executable)
|
Executable)
|
||||||
from wa.framework.workload import Workload, ApkUiautoWorkload
|
from wa.framework.workload import Workload, ApkUiautoWorkload, ReventWorkload
|
||||||
|
@ -15,21 +15,24 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
from wa import Command
|
||||||
from wa import Command, settings
|
from wa.framework import pluginloader
|
||||||
from wa.framework.configuration import RunConfiguration
|
from wa.framework.resource import ResourceResolver
|
||||||
from wa.framework.resource import Executable, NO_ONE, ResourceResolver
|
from wa.framework.target.manager import TargetManager
|
||||||
from wa.utils.revent import ReventRecorder
|
from wa.utils.revent import ReventRecorder
|
||||||
|
|
||||||
|
|
||||||
class RecordCommand(Command):
|
class RecordCommand(Command):
|
||||||
|
|
||||||
name = 'record'
|
name = 'record'
|
||||||
description = '''Performs a revent recording
|
description = '''
|
||||||
|
Performs a revent recording
|
||||||
|
|
||||||
This command helps making revent recordings. It will automatically
|
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.
|
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
|
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
|
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``.
|
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
|
- 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
|
recording is for, currently these are either ``setup``, ``run``, ``extract_results``
|
||||||
should be specified with the ``-s`` argument.
|
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):
|
def initialize(self, context):
|
||||||
self.parser.add_argument('-d', '--device', metavar='DEVICE',
|
self.parser.add_argument('-d', '--device', metavar='DEVICE',
|
||||||
help='''
|
help='''
|
||||||
@ -54,39 +65,204 @@ class RecordCommand(Command):
|
|||||||
take precedence over the device (if any)
|
take precedence over the device (if any)
|
||||||
specified in configuration.
|
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:
|
if args.device:
|
||||||
device = args.device
|
device = args.device
|
||||||
device_config = {}
|
device_config = {}
|
||||||
else:
|
else:
|
||||||
device = state.run_config.device
|
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)
|
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):
|
# Used to satisfy the workload API
|
||||||
resolver = ResourceResolver()
|
class LightContext(object):
|
||||||
resource = Executable(NO_ONE, abi, 'revent')
|
def __init__(self, tm):
|
||||||
return resolver.get(resource)
|
self.tm = tm
|
||||||
|
self.resolver = ResourceResolver()
|
||||||
|
self.resolver.load()
|
||||||
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')
|
|
||||||
|
@ -12,8 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
from wlauto.core.configuration.configuration import (settings,
|
from wa.framework.configuration.core import (settings,
|
||||||
RunConfiguration,
|
RunConfiguration,
|
||||||
JobGenerator,
|
JobGenerator,
|
||||||
ConfigurationPoint)
|
ConfigurationPoint)
|
||||||
from wlauto.core.configuration.plugin_cache import PluginCache
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from wlauto.core.configuration.configuration import MetaConfiguration, RunConfiguration
|
from wa.framework.configuration.core import MetaConfiguration, RunConfiguration
|
||||||
from wlauto.core.configuration.plugin_cache import PluginCache
|
from wa.framework.configuration.plugin_cache import PluginCache
|
||||||
from wlauto.utils.serializer import yaml
|
from wa.utils.serializer import yaml
|
||||||
from wlauto.utils.doc import strip_inlined_text
|
from wa.utils.doc import strip_inlined_text
|
||||||
|
|
||||||
DEFAULT_INSTRUMENTS = ['execution_time',
|
DEFAULT_INSTRUMENTS = ['execution_time',
|
||||||
'interrupts',
|
'interrupts',
|
||||||
|
@ -209,7 +209,7 @@ class Executor(object):
|
|||||||
"""
|
"""
|
||||||
The ``Executor``'s job is to set up the execution context and pass to a
|
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
|
``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.
|
returning.
|
||||||
|
|
||||||
The initial context set up involves combining configuration from various
|
The initial context set up involves combining configuration from various
|
||||||
@ -225,7 +225,7 @@ class Executor(object):
|
|||||||
self.error_logged = False
|
self.error_logged = False
|
||||||
self.warning_logged = False
|
self.warning_logged = False
|
||||||
pluginloader = None
|
pluginloader = None
|
||||||
self.device_manager = None
|
self.target_manager = None
|
||||||
self.device = None
|
self.device = None
|
||||||
|
|
||||||
def execute(self, config_manager, output):
|
def execute(self, config_manager, output):
|
||||||
@ -249,12 +249,12 @@ class Executor(object):
|
|||||||
output.write_config(config)
|
output.write_config(config)
|
||||||
|
|
||||||
self.logger.info('Connecting to target')
|
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)
|
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')
|
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')
|
self.logger.info('Generating jobs')
|
||||||
config_manager.generate_jobs(context)
|
config_manager.generate_jobs(context)
|
||||||
@ -262,7 +262,7 @@ class Executor(object):
|
|||||||
output.write_state()
|
output.write_state()
|
||||||
|
|
||||||
self.logger.info('Installing instrumentation')
|
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.install(instrument)
|
||||||
instrumentation.validate()
|
instrumentation.validate()
|
||||||
|
|
||||||
@ -361,6 +361,10 @@ class Runner(object):
|
|||||||
self.pm.process_run_output(self.context)
|
self.pm.process_run_output(self.context)
|
||||||
self.pm.export_run_output(self.context)
|
self.pm.export_run_output(self.context)
|
||||||
self.pm.finalize()
|
self.pm.finalize()
|
||||||
|
log.indent()
|
||||||
|
for job in self.context.completed_jobs:
|
||||||
|
job.finalize(self.context)
|
||||||
|
log.dedent()
|
||||||
|
|
||||||
def run_next_job(self, context):
|
def run_next_job(self, context):
|
||||||
job = context.start_job()
|
job = context.start_job()
|
||||||
|
@ -31,7 +31,7 @@ import requests
|
|||||||
|
|
||||||
from devlib.utils.android import ApkInfo
|
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.resource import ResourceGetter, SourcePriority, NO_ONE
|
||||||
from wa.framework.exception import ResourceError
|
from wa.framework.exception import ResourceError
|
||||||
from wa.utils.misc import (ensure_directory_exists as _d,
|
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)
|
path = os.path.join(basepath, 'bin', resource.abi, resource.filename)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
return 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)
|
files = get_by_extension(basepath, resource.kind)
|
||||||
return get_generic_resource(resource, files)
|
return get_generic_resource(resource, files)
|
||||||
|
|
||||||
@ -96,7 +105,7 @@ class Package(ResourceGetter):
|
|||||||
|
|
||||||
def get(self, resource):
|
def get(self, resource):
|
||||||
if resource.owner == NO_ONE:
|
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:
|
else:
|
||||||
modname = resource.owner.__module__
|
modname = resource.owner.__module__
|
||||||
basepath = os.path.dirname(sys.modules[modname].__file__)
|
basepath = os.path.dirname(sys.modules[modname].__file__)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import os
|
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
|
def init_user_directory(overwrite_existing=False): # pylint: disable=R0914
|
||||||
"""
|
"""
|
||||||
|
@ -26,7 +26,7 @@ from itertools import chain
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
from wa.framework.configuration.core import settings, ConfigurationPoint as Parameter
|
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)
|
ValidationError, ConfigError, HostError)
|
||||||
from wa.utils import log
|
from wa.utils import log
|
||||||
from wa.utils.misc import (ensure_directory_exists as _d, walk_modules, load_class,
|
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)
|
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
|
module with the specified name is not found, the loader must raise an
|
||||||
appropriate exception.
|
appropriate exception.
|
||||||
|
|
||||||
@ -743,10 +743,10 @@ class PluginLoader(object):
|
|||||||
self.logger.warning('Got: {}'.format(e))
|
self.logger.warning('Got: {}'.format(e))
|
||||||
else:
|
else:
|
||||||
msg = 'Failed to load {}'
|
msg = 'Failed to load {}'
|
||||||
raise LoaderError(msg.format(filepath), sys.exc_info())
|
raise PluginLoaderError(msg.format(filepath), sys.exc_info())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = 'Problem loading plugins from {}: {}'
|
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
|
def _discover_in_module(self, module): # NOQA pylint: disable=too-many-branches
|
||||||
self.logger.debug('Checking module %s', module.__name__)
|
self.logger.debug('Checking module %s', module.__name__)
|
||||||
|
@ -231,7 +231,7 @@ def connect(handler, signal, sender=dispatcher.Any, priority=0):
|
|||||||
|
|
||||||
.. note:: There is nothing that prevents instrumentation from sending their
|
.. note:: There is nothing that prevents instrumentation from sending their
|
||||||
own signals that are not part of the standard set. However the signal
|
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
|
: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
|
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.
|
:handler: The callback to be disconnected.
|
||||||
:signal: The signal the handler is to be disconnected form. It will
|
: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
|
:sender: If specified, the handler will only be disconnected from the signal
|
||||||
sent by this sender.
|
sent by this sender.
|
||||||
|
|
||||||
@ -284,7 +284,7 @@ def send(signal, sender=dispatcher.Anonymous, *args, **kwargs):
|
|||||||
|
|
||||||
Paramters:
|
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.
|
or its subclasses.
|
||||||
:sender: The sender of the signal (typically, this would be ``self``). Some handlers may only
|
:sender: The sender of the signal (typically, this would be ``self``). Some handlers may only
|
||||||
be subscribed to signals from a particular sender.
|
be subscribed to signals from a particular sender.
|
||||||
|
@ -33,6 +33,11 @@ def instantiate_target(tdesc, params, connect=None):
|
|||||||
|
|
||||||
tp, pp, cp = {}, {}, {}
|
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():
|
for name, value in params.iteritems():
|
||||||
if name in target_params:
|
if name in target_params:
|
||||||
tp[name] = value
|
tp[name] = value
|
||||||
@ -62,6 +67,8 @@ def instantiate_assistant(tdesc, params, target):
|
|||||||
for param in tdesc.assistant_params:
|
for param in tdesc.assistant_params:
|
||||||
if param.name in params:
|
if param.name in params:
|
||||||
assistant_params[param.name] = params[param.name]
|
assistant_params[param.name] = params[param.name]
|
||||||
|
elif param.default:
|
||||||
|
assistant_params[param.name] = param.default
|
||||||
return tdesc.assistant(target, **assistant_params)
|
return tdesc.assistant(target, **assistant_params)
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,8 +17,11 @@ import os
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from wa.framework.plugin import TargetedPlugin
|
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.framework.exception import WorkloadError
|
||||||
|
from wa.utils.revent import ReventRecorder
|
||||||
|
from wa.utils.exec_control import once
|
||||||
|
|
||||||
from devlib.utils.android import ApkInfo
|
from devlib.utils.android import ApkInfo
|
||||||
|
|
||||||
@ -64,7 +67,7 @@ class Workload(TargetedPlugin):
|
|||||||
def run(self, context):
|
def run(self, context):
|
||||||
"""
|
"""
|
||||||
Execute the workload. This is the method that performs the actual
|
Execute the workload. This is the method that performs the actual
|
||||||
"work" of the.
|
"work" of the workload.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -93,25 +96,29 @@ class Workload(TargetedPlugin):
|
|||||||
return '<Workload {}>'.format(self.name)
|
return '<Workload {}>'.format(self.name)
|
||||||
|
|
||||||
|
|
||||||
class ApkUiautoWorkload(Workload):
|
class ApkUIWorkload(Workload):
|
||||||
|
|
||||||
platform = 'android'
|
# May be optionally overwritten by subclasses
|
||||||
|
# Times are in seconds
|
||||||
|
loading_time = 10
|
||||||
|
|
||||||
def __init__(self, target, **kwargs):
|
def __init__(self, target, **kwargs):
|
||||||
super(ApkUiautoWorkload, self).__init__(target, **kwargs)
|
super(ApkUIWorkload, self).__init__(target, **kwargs)
|
||||||
self.apk = ApkHander(self)
|
self.apk = None
|
||||||
self.gui = UiAutomatorGUI(self)
|
self.gui = None
|
||||||
|
|
||||||
def init_resources(self, context):
|
def init_resources(self, context):
|
||||||
self.apk.init_resources(context.resolver)
|
self.apk.init_resources(context.resolver)
|
||||||
self.gui.init_resources(context.resolver)
|
self.gui.init_resources(context.resolver)
|
||||||
self.gui.init_commands()
|
self.gui.init_commands()
|
||||||
|
|
||||||
|
@once
|
||||||
def initialize(self, context):
|
def initialize(self, context):
|
||||||
self.gui.deploy()
|
self.gui.deploy()
|
||||||
|
|
||||||
def setup(self, context):
|
def setup(self, context):
|
||||||
self.apk.setup(context)
|
self.apk.setup(context)
|
||||||
|
time.sleep(self.loading_time)
|
||||||
self.gui.setup()
|
self.gui.setup()
|
||||||
|
|
||||||
def run(self, context):
|
def run(self, context):
|
||||||
@ -124,10 +131,40 @@ class ApkUiautoWorkload(Workload):
|
|||||||
self.gui.teardown()
|
self.gui.teardown()
|
||||||
self.apk.teardown()
|
self.apk.teardown()
|
||||||
|
|
||||||
|
@once
|
||||||
def finalize(self, context):
|
def finalize(self, context):
|
||||||
self.gui.remove()
|
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):
|
class UiAutomatorGUI(object):
|
||||||
|
|
||||||
stages = ['setup', 'runWorkload', 'extractResults', 'teardown']
|
stages = ['setup', 'runWorkload', 'extractResults', 'teardown']
|
||||||
@ -208,57 +245,92 @@ class UiAutomatorGUI(object):
|
|||||||
|
|
||||||
class ReventGUI(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.workload = workload
|
||||||
self.target = target
|
self.target = target
|
||||||
self.setup_timeout = setup_timeout
|
self.setup_timeout = setup_timeout
|
||||||
self.run_timeout = run_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_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_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.name))
|
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.logger = logging.getLogger('revent')
|
||||||
self.revent_setup_file = None
|
self.revent_setup_file = None
|
||||||
self.revent_run_file = None
|
self.revent_run_file = None
|
||||||
|
self.revent_extract_results_file = None
|
||||||
|
self.revent_teardown_file = None
|
||||||
|
|
||||||
def init_resources(self, context):
|
def init_resources(self, resolver):
|
||||||
self.revent_setup_file = context.resolver.get(ReventFile(self.workload, 'setup'))
|
self.revent_setup_file = resolver.get(ReventFile(owner=self.workload,
|
||||||
self.revent_run_file = context.resolver.get(ReventFile(self.workload, 'run'))
|
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):
|
def deploy(self):
|
||||||
self._check_revent_files(context)
|
self.revent_recorder.deploy()
|
||||||
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 run(self, context):
|
def setup(self):
|
||||||
command = '{} replay {}'.format(self.on_target_revent_binary, self.on_target_run_revent)
|
self._check_revent_files()
|
||||||
self.logger.debug('Replaying {}'.format(os.path.basename(self.on_target_run_revent)))
|
self.revent_recorder.replay(self.on_target_setup_revent,
|
||||||
self.target.execute(command, timeout=self.run_timeout)
|
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.')
|
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_setup_revent)
|
||||||
self.target.remove(self.on_target_run_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):
|
def remove(self):
|
||||||
# check the revent binary
|
self.revent_recorder.remove()
|
||||||
revent_binary = context.resolver.get(Executable(NO_ONE, self.target.abi, 'revent'))
|
|
||||||
if not os.path.isfile(revent_binary):
|
def init_commands(self):
|
||||||
message = '{} does not exist. '.format(revent_binary)
|
pass
|
||||||
message += 'Please build revent for your system and place it in that location'
|
|
||||||
raise WorkloadError(message)
|
def _check_revent_files(self):
|
||||||
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))
|
|
||||||
if not self.revent_run_file:
|
if not self.revent_run_file:
|
||||||
# pylint: disable=too-few-format-args
|
# pylint: disable=too-few-format-args
|
||||||
message = '{0}.run.revent file does not exist, Please provide one for your target, {0}'
|
message = '{0}.run.revent file does not exist, ' \
|
||||||
raise WorkloadError(message.format(self.target.name))
|
'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_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):
|
class ApkHander(object):
|
||||||
@ -280,10 +352,10 @@ class ApkHander(object):
|
|||||||
self.logcat_log = None
|
self.logcat_log = None
|
||||||
|
|
||||||
def init_resources(self, resolver):
|
def init_resources(self, resolver):
|
||||||
self.apk_file = resolver.get(ApkFile(self.owner,
|
self.apk_file = resolver.get(ApkFile(self.owner,
|
||||||
variant=self.variant,
|
variant=self.variant,
|
||||||
version=self.version),
|
version=self.version),
|
||||||
strict=self.strict)
|
strict=self.strict)
|
||||||
self.apk_info = ApkInfo(self.apk_file)
|
self.apk_info = ApkInfo(self.apk_file)
|
||||||
|
|
||||||
def setup(self, context):
|
def setup(self, context):
|
||||||
@ -332,7 +404,7 @@ class ApkHander(object):
|
|||||||
|
|
||||||
def start_activity(self):
|
def start_activity(self):
|
||||||
cmd = 'am start -W -n {}/{}'
|
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))
|
self.apk_info.activity))
|
||||||
if 'Error:' in output:
|
if 'Error:' in output:
|
||||||
# this will dismiss any error dialogs
|
# this will dismiss any error dialogs
|
||||||
|
@ -22,7 +22,7 @@ from unittest import TestCase
|
|||||||
|
|
||||||
from nose.tools import assert_equal
|
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):
|
class InterruptDiffTest(TestCase):
|
||||||
|
269
wa/tests/test_exec_control.py
Normal file
269
wa/tests/test_exec_control.py
Normal 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
110
wa/utils/exec_control.py
Normal 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
|
@ -54,7 +54,7 @@ from devlib.utils.misc import (ABI_MAP, check_output, walk_modules,
|
|||||||
check_output_logger = logging.getLogger('check_output')
|
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):
|
def diff_tokens(before_token, after_token):
|
||||||
"""
|
"""
|
||||||
Creates a diff of two tokens.
|
Creates a diff of two tokens.
|
||||||
|
@ -16,9 +16,11 @@
|
|||||||
from __future__ import division
|
from __future__ import division
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
|
import signal
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from wa.framework.resource import Executable, NO_ONE, ResourceResolver
|
||||||
|
|
||||||
GENERAL_MODE = 0
|
GENERAL_MODE = 0
|
||||||
GAMEPAD_MODE = 1
|
GAMEPAD_MODE = 1
|
||||||
@ -249,3 +251,38 @@ class ReventRecording(object):
|
|||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.close()
|
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)
|
||||||
|
27
wa/workloads/angrybirds_rio/__init__.py
Normal file
27
wa/workloads/angrybirds_rio/__init__.py
Normal 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.
|
||||||
|
"""
|
Loading…
x
Reference in New Issue
Block a user