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
This commit is contained in:
1
testrunner/.gitignore
vendored
1
testrunner/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
.*
|
||||||
|
|||||||
@@ -131,6 +131,17 @@ class AdbInterface:
|
|||||||
logger.Log("ADB Pull Failed: Source file %s does not exist." % src)
|
logger.Log("ADB Pull Failed: Source file %s does not exist." % src)
|
||||||
return False
|
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):
|
def DoesFileExist(self, src):
|
||||||
"""Checks if the given path exists on device target.
|
"""Checks if the given path exists on device target.
|
||||||
|
|
||||||
|
|||||||
@@ -289,25 +289,6 @@ def EnableCoverageBuild():
|
|||||||
os.environ["EMMA_INSTRUMENT"] = "true"
|
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():
|
def Run():
|
||||||
"""Does coverage operations based on command line args."""
|
"""Does coverage operations based on command line args."""
|
||||||
# TODO: do we want to support combining coverage for a single target
|
# TODO: do we want to support combining coverage for a single target
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ Do runtest --help to see full list of options.
|
|||||||
import glob
|
import glob
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from sets import Set
|
from sets import Set
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
@@ -73,6 +74,13 @@ class TestRunner(object):
|
|||||||
|
|
||||||
_DALVIK_VERIFIER_OFF_PROP = "dalvik.vm.dexopt-flags = v=n"
|
_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):
|
def __init__(self):
|
||||||
# disable logging of timestamp
|
# disable logging of timestamp
|
||||||
self._root_path = android_build.GetTop()
|
self._root_path = android_build.GetTop()
|
||||||
@@ -243,23 +251,11 @@ class TestRunner(object):
|
|||||||
self._adb.EnableAdbRoot()
|
self._adb.EnableAdbRoot()
|
||||||
else:
|
else:
|
||||||
logger.Log("adb root")
|
logger.Log("adb root")
|
||||||
rebuild_libcore = False
|
|
||||||
if target_set:
|
if target_set:
|
||||||
if self._options.coverage:
|
if self._options.coverage:
|
||||||
coverage.EnableCoverageBuild()
|
coverage.EnableCoverageBuild()
|
||||||
# hack to remove core library intermediates
|
target_set.append("external/emma/Android.mk")
|
||||||
# hack is needed because:
|
# TODO: detect if external/emma exists
|
||||||
# 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_build_string = " ".join(target_set)
|
target_build_string = " ".join(target_set)
|
||||||
extra_args_string = " ".join(extra_args_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,
|
target_build_string, self._options.make_jobs, self._root_path,
|
||||||
extra_args_string)
|
extra_args_string)
|
||||||
logger.Log(cmd)
|
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:
|
def _DoInstall(self, make_output):
|
||||||
# in preview mode, just display to the user what command would have been
|
"""Install artifacts from build onto device.
|
||||||
# run
|
|
||||||
logger.Log("adb sync")
|
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:
|
else:
|
||||||
# set timeout for build to 10 minutes, since libcore may need to
|
self._PushInstallFileToDevice(install_path)
|
||||||
# be rebuilt
|
|
||||||
run_command.RunCommand(cmd, return_output=False, timeout_time=600)
|
def _PushInstallFileToDevice(self, install_path):
|
||||||
logger.Log("Syncing to device...")
|
m = self._RE_MAKE_INSTALL_PATH.match(install_path)
|
||||||
self._adb.Sync(runtime_restart=rebuild_libcore)
|
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):
|
def _DoFullBuild(self, tests):
|
||||||
"""If necessary, run a full 'make' command for the tests that need it."""
|
"""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:
|
if not self._options.preview:
|
||||||
old_dir = os.getcwd()
|
old_dir = os.getcwd()
|
||||||
os.chdir(self._root_path)
|
os.chdir(self._root_path)
|
||||||
run_command.RunCommand(cmd, return_output=False)
|
output = run_command.RunCommand(cmd, return_output=False)
|
||||||
os.chdir(old_dir)
|
os.chdir(old_dir)
|
||||||
|
self._DoInstall(output)
|
||||||
|
|
||||||
def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
|
def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
|
||||||
if not test_suite.IsFullMake():
|
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,
|
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.
|
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
|
# to allow framework tests to access package-private framework api
|
||||||
framework_test = False
|
framework_test = False
|
||||||
for test in test_list:
|
for test in test_list:
|
||||||
|
|||||||
@@ -33,9 +33,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
|
|
||||||
DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
|
DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
|
||||||
|
|
||||||
# dependency on libcore (used for Emma)
|
|
||||||
_LIBCORE_BUILD_PATH = "libcore"
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
test_suite.AbstractTestSuite.__init__(self)
|
test_suite.AbstractTestSuite.__init__(self)
|
||||||
self._package_name = None
|
self._package_name = None
|
||||||
@@ -87,8 +84,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def GetBuildDependencies(self, options):
|
def GetBuildDependencies(self, options):
|
||||||
if options.coverage:
|
|
||||||
return [self._LIBCORE_BUILD_PATH]
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def Run(self, options, adb):
|
def Run(self, options, adb):
|
||||||
@@ -145,8 +140,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
logger.Log(adb_cmd)
|
logger.Log(adb_cmd)
|
||||||
elif options.coverage:
|
elif options.coverage:
|
||||||
coverage_gen = coverage.CoverageGenerator(adb)
|
coverage_gen = coverage.CoverageGenerator(adb)
|
||||||
adb.WaitForInstrumentation(self.GetPackageName(),
|
|
||||||
self.GetRunnerName())
|
|
||||||
# need to parse test output to determine path to coverage file
|
# need to parse test output to determine path to coverage file
|
||||||
logger.Log("Running in coverage mode, suppressing test output")
|
logger.Log("Running in coverage mode, suppressing test output")
|
||||||
try:
|
try:
|
||||||
@@ -168,8 +161,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
if coverage_file is not None:
|
if coverage_file is not None:
|
||||||
logger.Log("Coverage report generated at %s" % coverage_file)
|
logger.Log("Coverage report generated at %s" % coverage_file)
|
||||||
else:
|
else:
|
||||||
adb.WaitForInstrumentation(self.GetPackageName(),
|
|
||||||
self.GetRunnerName())
|
|
||||||
adb.StartInstrumentationNoResults(
|
adb.StartInstrumentationNoResults(
|
||||||
package_name=self.GetPackageName(),
|
package_name=self.GetPackageName(),
|
||||||
runner_name=self.GetRunnerName(),
|
runner_name=self.GetRunnerName(),
|
||||||
|
|||||||
Reference in New Issue
Block a user