diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py index 698ea8bc0..ea9188c01 100755 --- a/testrunner/adb_interface.py +++ b/testrunner/adb_interface.py @@ -355,13 +355,44 @@ class AdbInterface: "in test_defs.xml?" % instrumentation_path) 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. Blocks until device package manager is responding. Args: 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: WaitForResponseTimedOutError if package manager does not respond @@ -369,6 +400,13 @@ class AdbInterface: """ output = "" 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: output = self.SendCommand("sync", retry_count=retry_count) except errors.AbortError, e: @@ -389,10 +427,17 @@ class AdbInterface: # exception occurred that cannot be recovered from raise error 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 def GetSerialNumber(self): """Returns the serial number of the targeted device.""" return self.SendCommand("get-serialno").strip() - diff --git a/testrunner/coverage.py b/testrunner/coverage.py index 52e8a8ca8..4322e26db 100755 --- a/testrunner/coverage.py +++ b/testrunner/coverage.py @@ -62,28 +62,6 @@ class CoverageGenerator(object): self._adb = adb_interface 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, device_coverage_path, output_path=None, @@ -311,6 +289,25 @@ 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 8975303ea..6b03c79a2 100755 --- a/testrunner/runtest.py +++ b/testrunner/runtest.py @@ -157,7 +157,6 @@ class TestRunner(object): group.add_option("-s", "--serial", dest="serial", help="use specific serial") parser.add_option_group(group) - self._options, self._test_args = parser.parse_args() if (not self._options.only_list_tests @@ -231,9 +230,23 @@ class TestRunner(object): for test_suite in tests: self._AddBuildTarget(test_suite, target_set, extra_args_set) + 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_build_string = ' '.join(list(target_set)) extra_args_string = ' '.join(list(extra_args_set)) @@ -249,9 +262,11 @@ class TestRunner(object): # run logger.Log("adb sync") 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...") - self._adb.Sync() + self._adb.Sync(runtime_restart=rebuild_libcore) def _DoFullBuild(self, tests): """If necessary, run a full 'make' command for the tests that need it.""" diff --git a/testrunner/test_defs/instrumentation_test.py b/testrunner/test_defs/instrumentation_test.py index f0a8656bb..9d2f7798e 100644 --- a/testrunner/test_defs/instrumentation_test.py +++ b/testrunner/test_defs/instrumentation_test.py @@ -32,8 +32,8 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): DEFAULT_RUNNER = "android.test.InstrumentationTestRunner" - # build path to Emma target Makefile - _EMMA_BUILD_PATH = os.path.join("external", "emma") + # dependency on libcore (used for Emma) + _LIBCORE_BUILD_PATH = os.path.join("dalvik", "libcore") def __init__(self): test_suite.AbstractTestSuite.__init__(self) @@ -87,7 +87,7 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): def GetBuildDependencies(self, options): if options.coverage: - return [self._EMMA_BUILD_PATH] + return [self._LIBCORE_BUILD_PATH] return [] def Run(self, options, adb): @@ -144,8 +144,6 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): logger.Log(adb_cmd) elif options.coverage: coverage_gen = coverage.CoverageGenerator(adb) - if not coverage_gen.TestDeviceCoverageSupport(): - raise errors.AbortError adb.WaitForInstrumentation(self.GetPackageName(), self.GetRunnerName()) # need to parse test output to determine path to coverage file