Update from https://crrev.com/312600
TBR=jamesr@chromium.org
Review URL: https://codereview.chromium.org/863253002
diff --git a/build/android/pylib/base/test_run_factory.py b/build/android/pylib/base/test_run_factory.py
index 3f00caa..8c71ebb 100644
--- a/build/android/pylib/base/test_run_factory.py
+++ b/build/android/pylib/base/test_run_factory.py
@@ -9,7 +9,8 @@
from pylib.local.device import local_device_instrumentation_test_run
from pylib.remote.device import remote_device_environment
from pylib.remote.device import remote_device_gtest_run
-from pylib.remote.device import remote_device_uirobot_run
+from pylib.remote.device import remote_device_instrumentation_test_run
+from pylib.remote.device import remote_device_uirobot_test_run
from pylib.uirobot import uirobot_test_instance
@@ -24,10 +25,14 @@
if isinstance(env, remote_device_environment.RemoteDeviceEnvironment):
if isinstance(test_instance, gtest_test_instance.GtestTestInstance):
- return remote_device_gtest_run.RemoteDeviceGtestRun(env, test_instance)
- # TODO(rnephew): Add remote_device instrumentation test runs.
+ return remote_device_gtest_run.RemoteDeviceGtestTestRun(
+ env, test_instance)
+ if isinstance(test_instance,
+ instrumentation_test_instance.InstrumentationTestInstance):
+ return (remote_device_instrumentation_test_run
+ .RemoteDeviceInstrumentationTestRun(env, test_instance))
if isinstance(test_instance, uirobot_test_instance.UirobotTestInstance):
- return remote_device_uirobot_run.RemoteDeviceUirobotRun(
+ return remote_device_uirobot_test_run.RemoteDeviceUirobotTestRun(
env, test_instance)
diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py
index 50dc075..fb574e6 100644
--- a/build/android/pylib/constants.py
+++ b/build/android/pylib/constants.py
@@ -88,7 +88,7 @@
'android_webview_shell': PackageInfo(
'org.chromium.android_webview.shell',
'org.chromium.android_webview.shell.AwShellActivity',
- None,
+ '/data/local/tmp/android-webview-command-line',
None,
'org.chromium.android_webview.test'),
'gtest': PackageInfo(
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index fe9d2c6..a4ddef1 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -634,9 +634,12 @@
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- # Check that the package exists before clearing it. Necessary because
- # calling pm clear on a package that doesn't exist may never return.
- if self.GetApplicationPath(package):
+ # Check that the package exists before clearing it for android builds below
+ # JB MR2. Necessary because calling pm clear on a package that doesn't exist
+ # may never return.
+ if ((self.build_version_sdk >=
+ constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
+ or self.GetApplicationPath(package)):
self.RunShellCommand(['pm', 'clear', package], check_return=True)
@decorators.WithTimeoutAndRetriesFromInstance()
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py
index 58dd56a..72ab99c 100755
--- a/build/android/pylib/device/device_utils_test.py
+++ b/build/android/pylib/device/device_utils_test.py
@@ -930,19 +930,35 @@
class DeviceUtilsClearApplicationStateTest(DeviceUtilsNewImplTest):
def testClearApplicationState_packageDoesntExist(self):
- with self.assertCall(
- self.call.device.GetApplicationPath('this.package.does.not.exist'),
- None):
+ with self.assertCalls(
+ (self.call.adb.Shell('getprop ro.build.version.sdk'), '17\n'),
+ (self.call.device.GetApplicationPath('this.package.does.not.exist'),
+ None)):
+ self.device.ClearApplicationState('this.package.does.not.exist')
+
+ def testClearApplicationState_packageDoesntExistOnAndroidJBMR2OrAbove(self):
+ with self.assertCalls(
+ (self.call.adb.Shell('getprop ro.build.version.sdk'), '18\n'),
+ (self.call.adb.Shell('pm clear this.package.does.not.exist'),
+ 'Failed\r\n')):
self.device.ClearApplicationState('this.package.does.not.exist')
def testClearApplicationState_packageExists(self):
with self.assertCalls(
+ (self.call.adb.Shell('getprop ro.build.version.sdk'), '17\n'),
(self.call.device.GetApplicationPath('this.package.exists'),
'/data/app/this.package.exists.apk'),
(self.call.adb.Shell('pm clear this.package.exists'),
'Success\r\n')):
self.device.ClearApplicationState('this.package.exists')
+ def testClearApplicationState_packageExistsOnAndroidJBMR2OrAbove(self):
+ with self.assertCalls(
+ (self.call.adb.Shell('getprop ro.build.version.sdk'), '18\n'),
+ (self.call.adb.Shell('pm clear this.package.exists'),
+ 'Success\r\n')):
+ self.device.ClearApplicationState('this.package.exists')
+
class DeviceUtilsSendKeyEventTest(DeviceUtilsNewImplTest):
diff --git a/build/android/pylib/gtest/test_package_apk.py b/build/android/pylib/gtest/test_package_apk.py
index b4c67e6..cdb6daf 100644
--- a/build/android/pylib/gtest/test_package_apk.py
+++ b/build/android/pylib/gtest/test_package_apk.py
@@ -55,11 +55,11 @@
device.RunShellCommand('rm -f ' + self._GetFifo())
def _WatchFifo(self, device, timeout, logfile=None):
- for i in range(10):
+ for i in range(100):
if device.FileExists(self._GetFifo()):
- logging.info('Fifo created.')
+ logging.info('Fifo created. Slept for %f secs' % (i * 0.5))
break
- time.sleep(i)
+ time.sleep(0.5)
else:
raise device_errors.DeviceUnreachableError(
'Unable to find fifo on device %s ' % self._GetFifo())
@@ -67,14 +67,14 @@
args += ['shell', 'cat', self._GetFifo()]
return pexpect.spawn('adb', args, timeout=timeout, logfile=logfile)
- def _StartActivity(self, device):
+ def _StartActivity(self, device, force_stop=True):
device.StartActivity(
intent.Intent(package=self._package_info.package,
activity=self._package_info.activity,
action='android.intent.action.MAIN'),
# No wait since the runner waits for FIFO creation anyway.
blocking=False,
- force_stop=True)
+ force_stop=force_stop)
#override
def ClearApplicationState(self, device):
@@ -119,7 +119,10 @@
try:
self.tool.SetupEnvironment()
self._ClearFifo(device)
- self._StartActivity(device)
+ # Doesn't need to stop an Activity because ClearApplicationState() is
+ # always called before this call and so it is already stopped at this
+ # point.
+ self._StartActivity(device, force_stop=False)
finally:
self.tool.CleanUpEnvironment()
logfile = android_commands.NewLineNormalizer(sys.stdout)
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py
index fa38c4f..4bb9737 100644
--- a/build/android/pylib/gtest/test_runner.py
+++ b/build/android/pylib/gtest/test_runner.py
@@ -14,6 +14,18 @@
from pylib.local import local_test_server_spawner
from pylib.perf import perf_control
+# Test case statuses.
+RE_RUN = re.compile('\\[ RUN \\] ?(.*)\r\n')
+RE_FAIL = re.compile('\\[ FAILED \\] ?(.*?)( \\((\\d+) ms\\))?\r\r\n')
+RE_OK = re.compile('\\[ OK \\] ?(.*?)( \\((\\d+) ms\\))?\r\r\n')
+
+# Test run statuses.
+RE_PASSED = re.compile('\\[ PASSED \\] ?(.*)\r\n')
+RE_RUNNER_FAIL = re.compile('\\[ RUNNER_FAILED \\] ?(.*)\r\n')
+# Signal handlers are installed before starting tests
+# to output the CRASHED marker when a crash happens.
+RE_CRASH = re.compile('\\[ CRASHED \\](.*)\r\n')
+
def _TestSuiteRequiresMockTestServer(suite_name):
"""Returns True if the test suite requires mock test server."""
@@ -77,45 +89,33 @@
"""
results = base_test_result.TestRunResults()
- # Test case statuses.
- re_run = re.compile('\\[ RUN \\] ?(.*)\r\n')
- re_fail = re.compile('\\[ FAILED \\] ?(.*?)( \\((\\d+) ms\\))?\r\r\n')
- re_ok = re.compile('\\[ OK \\] ?(.*?)( \\((\\d+) ms\\))?\r\r\n')
-
- # Test run statuses.
- re_passed = re.compile('\\[ PASSED \\] ?(.*)\r\n')
- re_runner_fail = re.compile('\\[ RUNNER_FAILED \\] ?(.*)\r\n')
- # Signal handlers are installed before starting tests
- # to output the CRASHED marker when a crash happens.
- re_crash = re.compile('\\[ CRASHED \\](.*)\r\n')
-
log = ''
try:
while True:
full_test_name = None
- found = p.expect([re_run, re_passed, re_runner_fail],
+ found = p.expect([RE_RUN, RE_PASSED, RE_RUNNER_FAIL],
timeout=self._timeout)
- if found == 1: # re_passed
+ if found == 1: # RE_PASSED
break
- elif found == 2: # re_runner_fail
+ elif found == 2: # RE_RUNNER_FAIL
break
- else: # re_run
+ else: # RE_RUN
full_test_name = p.match.group(1).replace('\r', '')
- found = p.expect([re_ok, re_fail, re_crash], timeout=self._timeout)
+ found = p.expect([RE_OK, RE_FAIL, RE_CRASH], timeout=self._timeout)
log = p.before.replace('\r', '')
- if found == 0: # re_ok
+ if found == 0: # RE_OK
if full_test_name == p.match.group(1).replace('\r', ''):
duration_ms = int(p.match.group(3)) if p.match.group(3) else 0
results.AddResult(base_test_result.BaseTestResult(
full_test_name, base_test_result.ResultType.PASS,
duration=duration_ms, log=log))
- elif found == 2: # re_crash
+ elif found == 2: # RE_CRASH
results.AddResult(base_test_result.BaseTestResult(
full_test_name, base_test_result.ResultType.CRASH,
log=log))
break
- else: # re_fail
+ else: # RE_FAIL
duration_ms = int(p.match.group(3)) if p.match.group(3) else 0
results.AddResult(base_test_result.BaseTestResult(
full_test_name, base_test_result.ResultType.FAIL,
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py
index a87f81a..598967b 100644
--- a/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -5,6 +5,7 @@
import logging
import os
import pickle
+import re
import sys
from pylib import cmd_helper
@@ -24,6 +25,7 @@
_DEFAULT_ANNOTATIONS = [
'Smoke', 'SmallTest', 'MediumTest', 'LargeTest',
'EnormousTest', 'IntegrationTest']
+_NATIVE_CRASH_RE = re.compile('native crash', re.IGNORECASE)
_PICKLE_FORMAT_VERSION = 10
@@ -270,6 +272,10 @@
self._flags.extend([flag for flag in stripped_lines if flag])
@property
+ def suite(self):
+ return 'instrumentation'
+
+ @property
def apk_under_test(self):
return self._apk_under_test
@@ -459,6 +465,33 @@
return inflated_tests
@staticmethod
+ def GenerateMultiTestResult(errors, statuses):
+ INSTR_STATUS_CODE_START = 1
+ results = []
+ skip_counter = 1
+ for status_code, bundle in statuses:
+ if status_code != INSTR_STATUS_CODE_START:
+ # TODO(rnephew): Make skipped tests still output test name. This is only
+ # there to give skipped tests a unique name so they are counted
+ if 'test_skipped' in bundle:
+ test_name = str(skip_counter)
+ skip_counter += 1
+ else:
+ test_name = '%s#%s' % (
+ ''.join(bundle.get('class', [''])),
+ ''.join(bundle.get('test', [''])))
+
+ results.append(
+ GenerateTestResult(test_name, [(status_code, bundle)], 0, 0))
+ for error in errors:
+ if _NATIVE_CRASH_RE.search(error):
+ results.append(
+ base_test_result.BaseTestResult(
+ 'Crash detected', base_test_result.ResultType.CRASH))
+
+ return results
+
+ @staticmethod
def ParseAmInstrumentRawOutput(raw_output):
return ParseAmInstrumentRawOutput(raw_output)
diff --git a/build/android/pylib/remote/device/remote_device_gtest_run.py b/build/android/pylib/remote/device/remote_device_gtest_run.py
index e5f6990..76d1d45 100644
--- a/build/android/pylib/remote/device/remote_device_gtest_run.py
+++ b/build/android/pylib/remote/device/remote_device_gtest_run.py
@@ -24,7 +24,7 @@
'OnlyOutputFailures')
-class RemoteDeviceGtestRun(remote_device_test_run.RemoteDeviceTestRun):
+class RemoteDeviceGtestTestRun(remote_device_test_run.RemoteDeviceTestRun):
"""Run gtests and uirobot tests on a remote device."""
DEFAULT_RUNNER_PACKAGE = (
diff --git a/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py b/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
new file mode 100644
index 0000000..5138d46
--- /dev/null
+++ b/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
@@ -0,0 +1,55 @@
+# Copyright 2015 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.
+
+"""Run specific test on specific environment."""
+
+import logging
+import os
+import tempfile
+
+from pylib.base import base_test_result
+from pylib.remote.device import remote_device_test_run
+from pylib.utils import apk_helper
+
+
+class RemoteDeviceInstrumentationTestRun(
+ remote_device_test_run.RemoteDeviceTestRun):
+ """Run instrumentation tests on a remote device."""
+
+ #override
+ def TestPackage(self):
+ return self._test_instance.test_package
+
+ #override
+ def _TriggerSetUp(self):
+ """Set up the triggering of a test run."""
+ logging.info('Triggering test run.')
+ self._AmInstrumentTestSetup(
+ self._test_instance._apk_under_test, self._test_instance.test_apk,
+ self._test_instance.test_runner, environment_variables={})
+
+ #override
+ def _ParseTestResults(self):
+ logging.info('Parsing results from stdout.')
+ r = base_test_result.TestRunResults()
+
+ if self._results['results']['exception']:
+ r.AddResult(base_test_result.BaseTestResult(
+ self._results['results']['exception'],
+ base_test_result.ResultType.FAIL))
+ return r
+
+ _, errors, parsed_output = self._test_instance.ParseAmInstrumentRawOutput(
+ self._results['results']['output'].splitlines())
+ logging.debug(errors)
+ result = self._test_instance.GenerateMultiTestResult(errors, parsed_output)
+
+ if isinstance(result, base_test_result.BaseTestResult):
+ r.AddResult(result)
+ elif isinstance(result, list):
+ r.AddResults(result)
+ else:
+ raise Exception('Unexpected result type: %s' % type(result).__name__)
+
+ return r
diff --git a/build/android/pylib/remote/device/remote_device_test_run.py b/build/android/pylib/remote/device/remote_device_test_run.py
index 86ee587..c4f75b0 100644
--- a/build/android/pylib/remote/device/remote_device_test_run.py
+++ b/build/android/pylib/remote/device/remote_device_test_run.py
@@ -219,7 +219,7 @@
config['sdcard_files'] = ','.join(sdcard_files)
config['host_test'] = host_test
self._test_id = self._UploadTestToDevice(
- 'robotium', test_with_deps.name)
+ 'robotium', test_with_deps.name, app_id=self._app_id)
else:
self._test_id = self._UploadTestToDevice('robotium', test_path)
@@ -238,7 +238,7 @@
upload_results, 'Unable to upload %s.' % app_path)
return upload_results.json()['response']['app_id']
- def _UploadTestToDevice(self, test_type, test_path):
+ def _UploadTestToDevice(self, test_type, test_path, app_id=None):
"""Upload test to device
Args:
test_type: Type of test that is being uploaded. Ex. uirobot, gtest..
@@ -248,7 +248,7 @@
with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
logging.WARNING):
upload_results = appurify_sanitized.api.tests_upload(
- self._env.token, test_src, 'raw', test_type)
+ self._env.token, test_src, 'raw', test_type, app_id=app_id)
remote_device_helper.TestHttpResponse(upload_results,
'Unable to upload %s.' % test_path)
return upload_results.json()['response']['test_id']
diff --git a/build/android/pylib/remote/device/remote_device_uirobot_run.py b/build/android/pylib/remote/device/remote_device_uirobot_test_run.py
similarity index 91%
rename from build/android/pylib/remote/device/remote_device_uirobot_run.py
rename to build/android/pylib/remote/device/remote_device_uirobot_test_run.py
index 3aee343..7eedaa5 100644
--- a/build/android/pylib/remote/device/remote_device_uirobot_run.py
+++ b/build/android/pylib/remote/device/remote_device_uirobot_test_run.py
@@ -15,7 +15,7 @@
from pylib.remote.device import remote_device_helper
-class RemoteDeviceUirobotRun(remote_device_test_run.RemoteDeviceTestRun):
+class RemoteDeviceUirobotTestRun(remote_device_test_run.RemoteDeviceTestRun):
"""Run uirobot tests on a remote device."""
DEFAULT_RUNNER_TYPE = 'android_robot'
@@ -27,7 +27,7 @@
env: Environment the tests will run in.
test_instance: The test that will be run.
"""
- super(RemoteDeviceUirobotRun, self).__init__(env, test_instance)
+ super(RemoteDeviceUirobotTestRun, self).__init__(env, test_instance)
#override
def TestPackage(self):
diff --git a/build/android/pylib/uirobot/uirobot_test_instance.py b/build/android/pylib/uirobot/uirobot_test_instance.py
index a531b41..edd3200 100644
--- a/build/android/pylib/uirobot/uirobot_test_instance.py
+++ b/build/android/pylib/uirobot/uirobot_test_instance.py
@@ -18,7 +18,7 @@
"""
super(UirobotTestInstance, self).__init__()
self._apk_under_test = os.path.join(
- constants.GetOutDirectory(), args.apk_under_test)
+ constants.GetOutDirectory(), args.app_under_test)
self._minutes = args.minutes
self._package_name = apk_helper.GetPackageName(self._apk_under_test)
self._suite = 'Android Uirobot'
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index e92f851..4164bd8 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -142,7 +142,7 @@
help='Type of test to run as.')
group.add_argument('--runner-package', default='',
help='Package name of test.')
- group.add_argument('--apk-under-test', default='apks/Chrome.apk',
+ group.add_argument('--app-under-test', default='',
help='APK to run tests on.')
api_secret_group = group.add_mutually_exclusive_group()
@@ -316,6 +316,7 @@
AddCommonOptions(parser)
AddDeviceOptions(parser)
+ AddRemoteDeviceOptions(parser)
def ProcessInstrumentationOptions(args):