Switch monkey test to platform mode.
BUG=663127
Review-Url: https://codereview.chromium.org/2505713002
Cr-Commit-Position: refs/heads/master@{#432695}
diff --git a/build/android/pylib/base/test_instance_factory.py b/build/android/pylib/base/test_instance_factory.py
index 129bd7f..20b99d63 100644
--- a/build/android/pylib/base/test_instance_factory.py
+++ b/build/android/pylib/base/test_instance_factory.py
@@ -5,6 +5,7 @@
from pylib.gtest import gtest_test_instance
from pylib.instrumentation import instrumentation_test_instance
from pylib.junit import junit_test_instance
+from pylib.monkey import monkey_test_instance
from pylib.perf import perf_test_instance
from pylib.utils import isolator
@@ -19,6 +20,8 @@
args, isolator.Isolator(), error_func)
elif args.command == 'junit':
return junit_test_instance.JunitTestInstance(args, error_func)
+ elif args.command == 'monkey':
+ return monkey_test_instance.MonkeyTestInstance(args, error_func)
elif args.command == 'perf':
return perf_test_instance.PerfTestInstance(args, error_func)
diff --git a/build/android/pylib/base/test_run_factory.py b/build/android/pylib/base/test_run_factory.py
index d706fd4..0d71e97 100644
--- a/build/android/pylib/base/test_run_factory.py
+++ b/build/android/pylib/base/test_run_factory.py
@@ -5,9 +5,11 @@
from pylib.gtest import gtest_test_instance
from pylib.instrumentation import instrumentation_test_instance
from pylib.junit import junit_test_instance
+from pylib.monkey import monkey_test_instance
from pylib.local.device import local_device_environment
from pylib.local.device import local_device_gtest_run
from pylib.local.device import local_device_instrumentation_test_run
+from pylib.local.device import local_device_monkey_test_run
from pylib.local.device import local_device_perf_test_run
from pylib.local.machine import local_machine_environment
from pylib.local.machine import local_machine_junit_test_run
@@ -33,6 +35,9 @@
instrumentation_test_instance.InstrumentationTestInstance):
return (local_device_instrumentation_test_run
.LocalDeviceInstrumentationTestRun(env, test_instance))
+ if isinstance(test_instance, monkey_test_instance.MonkeyTestInstance):
+ return (local_device_monkey_test_run
+ .LocalDeviceMonkeyTestRun(env, test_instance))
if isinstance(test_instance,
perf_test_instance.PerfTestInstance):
return _CreatePerfTestRun(args, env, test_instance)
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 2705289f..7398f62 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -60,6 +60,7 @@
super(LocalDeviceInstrumentationTestRun, self).__init__(env, test_instance)
self._flag_changers = {}
+ #override
def TestPackage(self):
return self._test_instance.suite
diff --git a/build/android/pylib/local/device/local_device_monkey_test_run.py b/build/android/pylib/local/device/local_device_monkey_test_run.py
new file mode 100644
index 0000000..8f293c6
--- /dev/null
+++ b/build/android/pylib/local/device/local_device_monkey_test_run.py
@@ -0,0 +1,126 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from devil.android import device_errors
+from devil.android.sdk import intent
+from pylib import constants
+from pylib.base import base_test_result
+from pylib.local.device import local_device_test_run
+
+
+_CHROME_PACKAGE = constants.PACKAGE_INFO['chrome'].package
+
+class LocalDeviceMonkeyTestRun(local_device_test_run.LocalDeviceTestRun):
+ def __init__(self, env, test_instance):
+ super(LocalDeviceMonkeyTestRun, self).__init__(env, test_instance)
+
+ def TestPackage(self):
+ return 'monkey'
+
+ #override
+ def SetUp(self):
+ pass
+
+ #override
+ def _RunTest(self, device, test):
+ device.ClearApplicationState(self._test_instance.package)
+
+ # Chrome crashes are not always caught by Monkey test runner.
+ # Launch Chrome and verify Chrome has the same PID before and after
+ # the test.
+ device.StartActivity(
+ intent.Intent(package=self._test_instance.package,
+ activity=self._test_instance.activity,
+ action='android.intent.action.MAIN'),
+ blocking=True, force_stop=True)
+ before_pids = device.GetPids(self._test_instance.package)
+
+ output = ''
+ if before_pids:
+ if len(before_pids.get(self._test_instance.package, [])) > 1:
+ raise Exception(
+ 'At most one instance of process %s expected but found pids: '
+ '%s' % (self._test_instance.package, before_pids))
+ output = '\n'.join(self._LaunchMonkeyTest(device))
+ after_pids = device.GetPids(self._test_instance.package)
+
+ crashed = True
+ if not self._test_instance.package in before_pids:
+ logging.error('Failed to start the process.')
+ elif not self._test_instance.package in after_pids:
+ logging.error('Process %s has died.',
+ before_pids[self._test_instance.package])
+ elif (before_pids[self._test_instance.package] !=
+ after_pids[self._test_instance.package]):
+ logging.error('Detected process restart %s -> %s',
+ before_pids[self._test_instance.package],
+ after_pids[self._test_instance.package])
+ else:
+ crashed = False
+
+ success_pattern = 'Events injected: %d' % self._test_instance.event_count
+ if success_pattern in output and not crashed:
+ result = base_test_result.BaseTestResult(
+ test, base_test_result.ResultType.PASS, log=output)
+ else:
+ result = base_test_result.BaseTestResult(
+ test, base_test_result.ResultType.FAIL, log=output)
+ if 'chrome' in self._test_instance.package:
+ logging.warning('Starting MinidumpUploadService...')
+ # TODO(jbudorick): Update this after upstreaming.
+ minidump_intent = intent.Intent(
+ action='%s.crash.ACTION_FIND_ALL' % _CHROME_PACKAGE,
+ package=self._test_instance.package,
+ activity='%s.crash.MinidumpUploadService' % _CHROME_PACKAGE)
+ try:
+ device.RunShellCommand(
+ ['am', 'startservice'] + minidump_intent.am_args,
+ as_root=True, check_return=True)
+ except device_errors.CommandFailedError:
+ logging.exception('Failed to start MinidumpUploadService')
+
+ return result
+
+ #override
+ def TearDown(self):
+ pass
+
+ #override
+ def _CreateShards(self, tests):
+ return tests
+
+ #override
+ def _ShouldShard(self):
+ # TODO(mikecase): Run Monkey test concurrently on each attached device.
+ return False
+
+ #override
+ def _GetTests(self):
+ return ['MonkeyTest']
+
+ def _LaunchMonkeyTest(self, device):
+ try:
+ cmd = ['monkey',
+ '-p', self._test_instance.package,
+ '--throttle', str(self._test_instance.throttle),
+ '-s', str(self._test_instance.seed),
+ '--monitor-native-crashes',
+ '--kill-process-after-error']
+ for category in self._test_instance.categories:
+ cmd.extend(['-c', category])
+ for _ in range(self._test_instance.verbose_count):
+ cmd.append('-v')
+ cmd.append(str(self._test_instance.event_count))
+ return device.RunShellCommand(
+ cmd, timeout=self._test_instance.timeout)
+ finally:
+ try:
+ # Kill the monkey test process on the device. If you manually
+ # interupt the test run, this will prevent the monkey test from
+ # continuing to run.
+ device.KillAll('com.android.commands.monkey')
+ except device_errors.CommandFailedError:
+ pass
diff --git a/build/android/pylib/monkey/monkey_test_instance.py b/build/android/pylib/monkey/monkey_test_instance.py
new file mode 100644
index 0000000..4200ecd3
--- /dev/null
+++ b/build/android/pylib/monkey/monkey_test_instance.py
@@ -0,0 +1,72 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import random
+
+from pylib import constants
+from pylib.base import test_instance
+
+
+_SINGLE_EVENT_TIMEOUT = 100 # Milliseconds
+
+class MonkeyTestInstance(test_instance.TestInstance):
+
+ def __init__(self, args, _):
+ super(MonkeyTestInstance, self).__init__()
+
+ self._categories = args.categories
+ self._event_count = args.event_count
+ self._seed = args.seed or random.randint(1, 100)
+ self._throttle = args.throttle
+ self._verbose_count = args.verbose_count
+
+ self._package = constants.PACKAGE_INFO[args.browser].package
+ self._activity = constants.PACKAGE_INFO[args.browser].activity
+
+ self._timeout_ms = (self.event_count *
+ (self.throttle + _SINGLE_EVENT_TIMEOUT))
+
+ #override
+ def TestType(self):
+ return 'monkey'
+
+ #override
+ def SetUp(self):
+ pass
+
+ #override
+ def TearDown(self):
+ pass
+
+ @property
+ def activity(self):
+ return self._activity
+
+ @property
+ def categories(self):
+ return self._categories
+
+ @property
+ def event_count(self):
+ return self._event_count
+
+ @property
+ def package(self):
+ return self._package
+
+ @property
+ def seed(self):
+ return self._seed
+
+ @property
+ def throttle(self):
+ return self._throttle
+
+ @property
+ def timeout(self):
+ return self._timeout_ms
+
+ @property
+ def verbose_count(self):
+ return self._verbose_count
diff --git a/build/android/pylib/monkey/setup.py b/build/android/pylib/monkey/setup.py
deleted file mode 100644
index fe690a5..0000000
--- a/build/android/pylib/monkey/setup.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Generates test runner factory and tests for monkey tests."""
-
-from pylib.monkey import test_runner
-
-
-def Setup(test_options):
- """Create and return the test runner factory and tests.
-
- Args:
- test_options: A MonkeyOptions object.
-
- Returns:
- A tuple of (TestRunnerFactory, tests).
- """
- # Token to replicate across devices as the "test". The TestRunner does all of
- # the work to run the test.
- tests = ['MonkeyTest']
-
- def TestRunnerFactory(device, shard_index):
- return test_runner.TestRunner(
- test_options, device, shard_index)
-
- return (TestRunnerFactory, tests)
diff --git a/build/android/pylib/monkey/test_options.py b/build/android/pylib/monkey/test_options.py
deleted file mode 100644
index 54d3d084..0000000
--- a/build/android/pylib/monkey/test_options.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Defines the MonkeyOptions named tuple."""
-
-import collections
-
-MonkeyOptions = collections.namedtuple('MonkeyOptions', [
- 'verbose_count',
- 'package',
- 'event_count',
- 'category',
- 'throttle',
- 'seed',
- 'extra_args'])
diff --git a/build/android/pylib/monkey/test_runner.py b/build/android/pylib/monkey/test_runner.py
deleted file mode 100644
index ff4c9400..0000000
--- a/build/android/pylib/monkey/test_runner.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Runs a monkey test on a single device."""
-
-import logging
-import random
-
-from devil.android import device_errors
-from devil.android.sdk import intent
-from pylib import constants
-from pylib.base import base_test_result
-from pylib.base import base_test_runner
-
-_CHROME_PACKAGE = constants.PACKAGE_INFO['chrome'].package
-
-class TestRunner(base_test_runner.BaseTestRunner):
- """A TestRunner instance runs a monkey test on a single device."""
-
- def __init__(self, test_options, device, _):
- super(TestRunner, self).__init__(device, None)
- self._options = test_options
- self._package = constants.PACKAGE_INFO[self._options.package].package
- self._activity = constants.PACKAGE_INFO[self._options.package].activity
-
- def _LaunchMonkeyTest(self):
- """Runs monkey test for a given package.
-
- Returns:
- Output from the monkey command on the device.
- """
-
- timeout_ms = self._options.event_count * self._options.throttle * 1.5
-
- cmd = ['monkey',
- '-p %s' % self._package,
- ' '.join(['-c %s' % c for c in self._options.category]),
- '--throttle %d' % self._options.throttle,
- '-s %d' % (self._options.seed or random.randint(1, 100)),
- '-v ' * self._options.verbose_count,
- '--monitor-native-crashes',
- '--kill-process-after-error',
- self._options.extra_args,
- '%d' % self._options.event_count]
- return self.device.RunShellCommand(' '.join(cmd), timeout=timeout_ms)
-
- def RunTest(self, test_name):
- """Run a Monkey test on the device.
-
- Args:
- test_name: String to use for logging the test result.
-
- Returns:
- A tuple of (TestRunResults, retry).
- """
- self.device.StartActivity(
- intent.Intent(package=self._package, activity=self._activity,
- action='android.intent.action.MAIN'),
- blocking=True, force_stop=True)
-
- # Chrome crashes are not always caught by Monkey test runner.
- # Verify Chrome has the same PID before and after the test.
- before_pids = self.device.GetPids(self._package)
-
- # Run the test.
- output = ''
- if before_pids:
- if len(before_pids.get(self._package, [])) > 1:
- raise Exception(
- 'At most one instance of process %s expected but found pids: '
- '%s' % (self._package, before_pids))
- output = '\n'.join(self._LaunchMonkeyTest())
- after_pids = self.device.GetPids(self._package)
-
- crashed = True
- if not self._package in before_pids:
- logging.error('Failed to start the process.')
- elif not self._package in after_pids:
- logging.error('Process %s has died.', before_pids[self._package])
- elif before_pids[self._package] != after_pids[self._package]:
- logging.error('Detected process restart %s -> %s',
- before_pids[self._package], after_pids[self._package])
- else:
- crashed = False
-
- results = base_test_result.TestRunResults()
- success_pattern = 'Events injected: %d' % self._options.event_count
- if success_pattern in output and not crashed:
- result = base_test_result.BaseTestResult(
- test_name, base_test_result.ResultType.PASS, log=output)
- else:
- result = base_test_result.BaseTestResult(
- test_name, base_test_result.ResultType.FAIL, log=output)
- if 'chrome' in self._options.package:
- logging.warning('Starting MinidumpUploadService...')
- # TODO(jbudorick): Update this after upstreaming.
- minidump_intent = intent.Intent(
- action='%s.crash.ACTION_FIND_ALL' % _CHROME_PACKAGE,
- package=self._package,
- activity='%s.crash.MinidumpUploadService' % _CHROME_PACKAGE)
- try:
- self.device.RunShellCommand(
- ['am', 'startservice'] + minidump_intent.am_args,
- as_root=True, check_return=True)
- except device_errors.CommandFailedError:
- logging.exception('Failed to start MinidumpUploadService')
-
- results.AddResult(result)
- return results, False
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index fe9bdbb9..d44d9e3 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -36,8 +36,6 @@
from pylib.linker import setup as linker_setup
from pylib.junit import setup as junit_setup
from pylib.junit import test_dispatcher as junit_dispatcher
-from pylib.monkey import setup as monkey_setup
-from pylib.monkey import test_options as monkey_test_options
from pylib.results import json_results
from pylib.results import report_results
@@ -426,53 +424,32 @@
group = parser.add_argument_group('Monkey Test Options')
group.add_argument(
- '--package', required=True, choices=constants.PACKAGE_INFO.keys(),
- metavar='PACKAGE', help='Package under test.')
+ '--browser', required=True, choices=constants.PACKAGE_INFO.keys(),
+ metavar='BROWSER', help='Browser under test.')
group.add_argument(
'--event-count', default=10000, type=int,
help='Number of events to generate (default: %(default)s).')
group.add_argument(
- '--category', default='',
- help='A list of allowed categories.')
+ '--category', nargs='*', dest='categories', default=[],
+ help='A list of allowed categories. Monkey will only visit activities '
+ 'that are listed with one of the specified categories.')
group.add_argument(
'--throttle', default=100, type=int,
help='Delay between events (ms) (default: %(default)s). ')
group.add_argument(
'--seed', type=int,
- help=('Seed value for pseudo-random generator. Same seed value generates '
- 'the same sequence of events. Seed is randomized by default.'))
+ help='Seed value for pseudo-random generator. Same seed value generates '
+ 'the same sequence of events. Seed is randomized by default.')
group.add_argument(
- '--extra-args', default='',
- help=('String of other args to pass to the command verbatim.'))
-
+ '--repeat', dest='repeat', type=int, default=0,
+ help='Number of times to repeat the specified set of tests.')
+ group.add_argument(
+ '--break-on-failure', '--break_on_failure',
+ dest='break_on_failure', action='store_true',
+ help='Whether to break on failure.')
AddCommonOptions(parser)
AddDeviceOptions(parser)
-def ProcessMonkeyTestOptions(args):
- """Processes all monkey test options.
-
- Args:
- args: argparse.Namespace object.
-
- Returns:
- A MonkeyOptions named tuple which contains all options relevant to
- monkey tests.
- """
- # TODO(jbudorick): Handle this directly in argparse with nargs='+'
- category = args.category
- if category:
- category = args.category.split(',')
-
- # TODO(jbudorick): Get rid of MonkeyOptions.
- return monkey_test_options.MonkeyOptions(
- args.verbose_count,
- args.package,
- args.event_count,
- category,
- args.throttle,
- args.seed,
- args.extra_args)
-
def AddPerfTestOptions(parser):
"""Adds perf test options to |parser|."""
@@ -608,27 +585,6 @@
return exit_code
-def _RunMonkeyTests(args, devices):
- """Subcommand of RunTestsCommands which runs monkey tests."""
- monkey_options = ProcessMonkeyTestOptions(args)
-
- runner_factory, tests = monkey_setup.Setup(monkey_options)
-
- results, exit_code = test_dispatcher.RunTests(
- tests, runner_factory, devices, shard=False, test_timeout=None,
- num_retries=args.num_retries)
-
- report_results.LogFull(
- results=results,
- test_type='Monkey',
- test_package='Monkey')
-
- if args.json_results_file:
- json_results.GenerateJsonResultsFile([results], args.json_results_file)
-
- return exit_code
-
-
def _RunPythonTests(args):
"""Subcommand of RunTestsCommand which runs python unit tests."""
suite_vars = constants.PYTHON_UNIT_TEST_SUITES[args.suite_name]
@@ -678,7 +634,7 @@
return sorted(attached_devices)
-_DEFAULT_PLATFORM_MODE_TESTS = ['gtest', 'instrumentation', 'perf']
+_DEFAULT_PLATFORM_MODE_TESTS = ['gtest', 'instrumentation', 'monkey', 'perf']
def RunTestsCommand(args): # pylint: disable=too-many-return-statements
@@ -718,8 +674,6 @@
return _RunLinkerTests(args, get_devices())
elif command == 'junit':
return _RunJUnitTests(args)
- elif command == 'monkey':
- return _RunMonkeyTests(args, get_devices())
elif command == 'python':
return _RunPythonTests(args)
else:
@@ -731,6 +685,7 @@
'gtest',
'instrumentation',
'junit',
+ 'monkey',
'perf',
]
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 8bc5786..a9906bee 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -106,6 +106,7 @@
pylib/local/device/local_device_environment.py
pylib/local/device/local_device_gtest_run.py
pylib/local/device/local_device_instrumentation_test_run.py
+pylib/local/device/local_device_monkey_test_run.py
pylib/local/device/local_device_perf_test_run.py
pylib/local/device/local_device_test_run.py
pylib/local/local_test_server_spawner.py
@@ -113,9 +114,7 @@
pylib/local/machine/local_machine_environment.py
pylib/local/machine/local_machine_junit_test_run.py
pylib/monkey/__init__.py
-pylib/monkey/setup.py
-pylib/monkey/test_options.py
-pylib/monkey/test_runner.py
+pylib/monkey/monkey_test_instance.py
pylib/perf/__init__.py
pylib/perf/perf_test_instance.py
pylib/results/__init__.py