From 7454171f1755187a2f3d0a8d9d7229a638b32f01 Mon Sep 17 00:00:00 2001 From: Brett Chabot Date: Fri, 31 Aug 2012 18:39:00 -0700 Subject: [PATCH] Coverage and unbundling changes for runtest. Stop using 'adb sync' in runtest. It was unreliable even when working with full platform builds, and doesn't work at all for unbundled apps. Instead, use output from build command to find produced artifacts and use 'adb install' where possible. However, note that this approach won't sync previously built artifacts to device. Also adjust to build system support for code coverage. Its no longer required to build libcore to get code coverage. Change-Id: I9c5d37897c9570d2d29db3ec82f5c53e60a8f485 --- testrunner/.gitignore | 1 + testrunner/adb_interface.py | 11 +++ testrunner/coverage.py | 19 ------ testrunner/runtest.py | 70 ++++++++++++-------- testrunner/test_defs/instrumentation_test.py | 9 --- 5 files changed, 56 insertions(+), 54 deletions(-) diff --git a/testrunner/.gitignore b/testrunner/.gitignore index 0d20b6487..b3421b024 100644 --- a/testrunner/.gitignore +++ b/testrunner/.gitignore @@ -1 +1,2 @@ *.pyc +.* diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py index ddc50d5b8..519134014 100755 --- a/testrunner/adb_interface.py +++ b/testrunner/adb_interface.py @@ -131,6 +131,17 @@ class AdbInterface: logger.Log("ADB Pull Failed: Source file %s does not exist." % src) return False + def Install(self, apk_path): + """Installs apk on device. + + Args: + apk_path: file path to apk file on host + + Returns: + output of install command + """ + return self.SendCommand("install -r %s" % apk_path) + def DoesFileExist(self, src): """Checks if the given path exists on device target. diff --git a/testrunner/coverage.py b/testrunner/coverage.py index 4322e26db..1c8ec2fd5 100755 --- a/testrunner/coverage.py +++ b/testrunner/coverage.py @@ -289,25 +289,6 @@ def EnableCoverageBuild(): os.environ["EMMA_INSTRUMENT"] = "true" -def TestDeviceCoverageSupport(adb): - """Check if device has support for generating code coverage metrics. - - This tries to dump emma help information on device, a response containing - help information will indicate that emma is already on system class path. - - Returns: - True if device can support code coverage. False otherwise. - """ - try: - output = adb.SendShellCommand("exec app_process / emma -h") - - if output.find('emma usage:') == 0: - return True - except errors.AbortError: - pass - return False - - def Run(): """Does coverage operations based on command line args.""" # TODO: do we want to support combining coverage for a single target diff --git a/testrunner/runtest.py b/testrunner/runtest.py index 6c1b1b6c1..9082d79e2 100755 --- a/testrunner/runtest.py +++ b/testrunner/runtest.py @@ -33,6 +33,7 @@ Do runtest --help to see full list of options. import glob import optparse import os +import re from sets import Set import sys import time @@ -73,6 +74,13 @@ class TestRunner(object): _DALVIK_VERIFIER_OFF_PROP = "dalvik.vm.dexopt-flags = v=n" + # regular expression to match install: statements in make output + _RE_MAKE_INSTALL = re.compile(r'Install:\s(.+)') + + # regular expression to find remote device path from a file path relative + # to build root + _RE_MAKE_INSTALL_PATH = re.compile(r'out\/target\/product\/\w+\/(.+)$') + def __init__(self): # disable logging of timestamp self._root_path = android_build.GetTop() @@ -243,23 +251,11 @@ class TestRunner(object): self._adb.EnableAdbRoot() else: logger.Log("adb root") - rebuild_libcore = False if target_set: if self._options.coverage: coverage.EnableCoverageBuild() - # hack to remove core library intermediates - # hack is needed because: - # 1. EMMA_INSTRUMENT changes what source files to include in libcore - # but it does not trigger a rebuild - # 2. there's no target (like "clear-intermediates") to remove the files - # decently - rebuild_libcore = not coverage.TestDeviceCoverageSupport(self._adb) - if rebuild_libcore: - cmd = "rm -rf %s" % os.path.join( - self._root_path, - "out/target/common/obj/JAVA_LIBRARIES/core_intermediates/") - logger.Log(cmd) - run_command.RunCommand(cmd, return_output=False) + target_set.append("external/emma/Android.mk") + # TODO: detect if external/emma exists target_build_string = " ".join(target_set) extra_args_string = " ".join(extra_args_set) @@ -270,17 +266,38 @@ class TestRunner(object): target_build_string, self._options.make_jobs, self._root_path, extra_args_string) logger.Log(cmd) + if not self._options.preview: + output = run_command.RunCommand(cmd, return_output=True, timeout_time=600) + self._DoInstall(output) - if self._options.preview: - # in preview mode, just display to the user what command would have been - # run - logger.Log("adb sync") - else: - # set timeout for build to 10 minutes, since libcore may need to - # be rebuilt - run_command.RunCommand(cmd, return_output=False, timeout_time=600) - logger.Log("Syncing to device...") - self._adb.Sync(runtime_restart=rebuild_libcore) + def _DoInstall(self, make_output): + """Install artifacts from build onto device. + + Looks for 'install:' text from make output to find artifacts to install. + + Args: + make_output: stdout from make command + """ + for line in make_output.split("\n"): + m = self._RE_MAKE_INSTALL.match(line) + if m: + install_path = m.group(1) + if install_path.endswith(".apk"): + abs_install_path = os.path.join(self._root_path, install_path) + logger.Log("adb install -r %s" % abs_install_path) + logger.Log(self._adb.Install(abs_install_path)) + else: + self._PushInstallFileToDevice(install_path) + + def _PushInstallFileToDevice(self, install_path): + m = self._RE_MAKE_INSTALL_PATH.match(install_path) + if m: + remote_path = m.group(1) + abs_install_path = os.path.join(self._root_path, install_path) + logger.Log("adb push %s %s", abs_install_path, remote_path) + self._adb.Push(abs_install_path, remote_path) + else: + logger.Log("Error: Failed to recognize path of file to install %s" % install_path) def _DoFullBuild(self, tests): """If necessary, run a full 'make' command for the tests that need it.""" @@ -309,8 +326,9 @@ class TestRunner(object): if not self._options.preview: old_dir = os.getcwd() os.chdir(self._root_path) - run_command.RunCommand(cmd, return_output=False) + output = run_command.RunCommand(cmd, return_output=False) os.chdir(old_dir) + self._DoInstall(output) def _AddBuildTarget(self, test_suite, target_set, extra_args_set): if not test_suite.IsFullMake(): @@ -369,7 +387,7 @@ class TestRunner(object): If one or more tests needs dalvik verifier off, and it is not already off, turns off verifier and reboots device to allow change to take effect. """ - # hack to check if these are framework/base tests. If so, turn off verifier + # hack to check if these are frameworks/base tests. If so, turn off verifier # to allow framework tests to access package-private framework api framework_test = False for test in test_list: diff --git a/testrunner/test_defs/instrumentation_test.py b/testrunner/test_defs/instrumentation_test.py index ede7ceb26..12633b269 100644 --- a/testrunner/test_defs/instrumentation_test.py +++ b/testrunner/test_defs/instrumentation_test.py @@ -33,9 +33,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): DEFAULT_RUNNER = "android.test.InstrumentationTestRunner" - # dependency on libcore (used for Emma) - _LIBCORE_BUILD_PATH = "libcore" - def __init__(self): test_suite.AbstractTestSuite.__init__(self) self._package_name = None @@ -87,8 +84,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): return self def GetBuildDependencies(self, options): - if options.coverage: - return [self._LIBCORE_BUILD_PATH] return [] def Run(self, options, adb): @@ -145,8 +140,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): logger.Log(adb_cmd) elif options.coverage: coverage_gen = coverage.CoverageGenerator(adb) - adb.WaitForInstrumentation(self.GetPackageName(), - self.GetRunnerName()) # need to parse test output to determine path to coverage file logger.Log("Running in coverage mode, suppressing test output") try: @@ -168,8 +161,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): if coverage_file is not None: logger.Log("Coverage report generated at %s" % coverage_file) else: - adb.WaitForInstrumentation(self.GetPackageName(), - self.GetRunnerName()) adb.StartInstrumentationNoResults( package_name=self.GetPackageName(), runner_name=self.GetRunnerName(),