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:
@@ -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)
|
||||||
self.WaitForDevicePm()
|
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()
|
||||||
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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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."""
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user