code coverage improvement

* automatically detect if emma is on device
* build emma into libcore if necessary
* stop runtime before adb sync and restart afterwards

Change-Id: I6f1beacdd266310c481351165a054dca8f8657b2
This commit is contained in:
Guang Zhu
2010-04-15 10:48:26 -07:00
parent 5a076f43d5
commit 8aff3602e7
4 changed files with 88 additions and 33 deletions

View File

@@ -355,13 +355,44 @@ class AdbInterface:
"in test_defs.xml?" % instrumentation_path) "in test_defs.xml?" % instrumentation_path)
raise errors.WaitForResponseTimedOutError() raise errors.WaitForResponseTimedOutError()
def Sync(self, retry_count=3): def WaitForBootComplete(self, wait_time=120):
"""Waits for targeted device's bootcomplete flag to be set.
Args:
wait_time: time in seconds to wait
Raises:
WaitForResponseTimedOutError if wait_time elapses and pm still does not
respond.
"""
logger.Log("Waiting for boot complete...")
self.SendCommand("wait-for-device")
# Now the device is there, but may not be running.
# Query the package manager with a basic command
boot_complete = False
attempts = 0
wait_period = 5
while not boot_complete and (attempts*wait_period) < wait_time:
output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1)
output = output.strip()
if output == "1":
boot_complete = True
else:
time.sleep(wait_period)
attempts += 1
if not boot_complete:
raise errors.WaitForResponseTimedOutError(
"dev.bootcomplete flag was not set after %s seconds" % wait_time)
def Sync(self, retry_count=3, runtime_restart=False):
"""Perform a adb sync. """Perform a adb sync.
Blocks until device package manager is responding. Blocks until device package manager is responding.
Args: Args:
retry_count: number of times to retry sync before failing retry_count: number of times to retry sync before failing
runtime_restart: stop runtime during sync and restart afterwards, useful
for syncing system libraries (core, framework etc)
Raises: Raises:
WaitForResponseTimedOutError if package manager does not respond WaitForResponseTimedOutError if package manager does not respond
@@ -369,6 +400,13 @@ class AdbInterface:
""" """
output = "" output = ""
error = None error = None
if runtime_restart:
self.SendShellCommand("setprop ro.monkey 1", retry_count=retry_count)
# manual rest bootcomplete flag
self.SendShellCommand("setprop dev.bootcomplete 0",
retry_count=retry_count)
self.SendShellCommand("stop", retry_count=retry_count)
try: try:
output = self.SendCommand("sync", retry_count=retry_count) output = self.SendCommand("sync", retry_count=retry_count)
except errors.AbortError, e: except errors.AbortError, e:
@@ -389,10 +427,17 @@ class AdbInterface:
# exception occurred that cannot be recovered from # exception occurred that cannot be recovered from
raise error raise error
logger.SilentLog(output) logger.SilentLog(output)
if runtime_restart:
# start runtime and wait till boot complete flag is set
self.SendShellCommand("start", retry_count=retry_count)
self.WaitForBootComplete()
# press the MENU key, this will disable key guard if runtime is started
# with ro.monkey set to 1
self.SendShellCommand("input keyevent 82", retry_count=retry_count)
else:
self.WaitForDevicePm() self.WaitForDevicePm()
return output return output
def GetSerialNumber(self): def GetSerialNumber(self):
"""Returns the serial number of the targeted device.""" """Returns the serial number of the targeted device."""
return self.SendCommand("get-serialno").strip() return self.SendCommand("get-serialno").strip()

View File

@@ -62,28 +62,6 @@ class CoverageGenerator(object):
self._adb = adb_interface self._adb = adb_interface
self._targets_manifest = self._ReadTargets() self._targets_manifest = self._ReadTargets()
def TestDeviceCoverageSupport(self):
"""Check if device has support for generating code coverage metrics.
Currently this will check if the emma.jar file is on the device's boot
classpath.
Returns:
True if device can support code coverage. False otherwise.
"""
try:
output = self._adb.SendShellCommand("cat init.rc | grep BOOTCLASSPATH | "
"grep emma.jar")
if len(output) > 0:
return True
except errors.AbortError:
pass
logger.Log("Error: Targeted device does not have emma.jar on its "
"BOOTCLASSPATH.")
logger.Log("Modify the BOOTCLASSPATH entry in system/core/rootdir/init.rc"
" to add emma.jar")
return False
def ExtractReport(self, test_suite, def ExtractReport(self, test_suite,
device_coverage_path, device_coverage_path,
output_path=None, output_path=None,
@@ -311,6 +289,25 @@ 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

View File

@@ -157,7 +157,6 @@ class TestRunner(object):
group.add_option("-s", "--serial", dest="serial", group.add_option("-s", "--serial", dest="serial",
help="use specific serial") help="use specific serial")
parser.add_option_group(group) parser.add_option_group(group)
self._options, self._test_args = parser.parse_args() self._options, self._test_args = parser.parse_args()
if (not self._options.only_list_tests if (not self._options.only_list_tests
@@ -231,9 +230,23 @@ class TestRunner(object):
for test_suite in tests: for test_suite in tests:
self._AddBuildTarget(test_suite, target_set, extra_args_set) self._AddBuildTarget(test_suite, target_set, extra_args_set)
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
# 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_build_string = ' '.join(list(target_set)) target_build_string = ' '.join(list(target_set))
extra_args_string = ' '.join(list(extra_args_set)) extra_args_string = ' '.join(list(extra_args_set))
@@ -249,9 +262,11 @@ class TestRunner(object):
# run # run
logger.Log("adb sync") logger.Log("adb sync")
else: else:
run_command.RunCommand(cmd, return_output=False) # 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...") logger.Log("Syncing to device...")
self._adb.Sync() self._adb.Sync(runtime_restart=rebuild_libcore)
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."""

View File

@@ -32,8 +32,8 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
DEFAULT_RUNNER = "android.test.InstrumentationTestRunner" DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
# build path to Emma target Makefile # dependency on libcore (used for Emma)
_EMMA_BUILD_PATH = os.path.join("external", "emma") _LIBCORE_BUILD_PATH = os.path.join("dalvik", "libcore")
def __init__(self): def __init__(self):
test_suite.AbstractTestSuite.__init__(self) test_suite.AbstractTestSuite.__init__(self)
@@ -87,7 +87,7 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
def GetBuildDependencies(self, options): def GetBuildDependencies(self, options):
if options.coverage: if options.coverage:
return [self._EMMA_BUILD_PATH] return [self._LIBCORE_BUILD_PATH]
return [] return []
def Run(self, options, adb): def Run(self, options, adb):
@@ -144,8 +144,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)
if not coverage_gen.TestDeviceCoverageSupport():
raise errors.AbortError
adb.WaitForInstrumentation(self.GetPackageName(), adb.WaitForInstrumentation(self.GetPackageName(),
self.GetRunnerName()) self.GetRunnerName())
# need to parse test output to determine path to coverage file # need to parse test output to determine path to coverage file