1 #!/usr/bin/env python 2 # 3 # Copyright 2008, The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 """Command line utility for running Android tests 18 19 runtest helps automate the instructions for building and running tests 20 - It builds the corresponding test package for the code you want to test 21 - It pushes the test package to your device or emulator 22 - It launches InstrumentationTestRunner (or similar) to run the tests you 23 specify. 24 25 runtest supports running tests whose attributes have been pre-defined in 26 _TEST_FILE_NAME files, (runtest <testname>), or by specifying the file 27 system path to the test to run (runtest --path <path>). 28 29 Do runtest --help to see full list of options. 30 """ 31 32 # Python imports 33 import glob 34 import optparse 35 import os 36 import re 37 from sets import Set 38 import sys 39 import time 40 41 # local imports 42 import adb_interface 43 import android_build 44 from coverage import coverage 45 import errors 46 import logger 47 import make_tree 48 import run_command 49 from test_defs import test_defs 50 from test_defs import test_walker 51 52 53 class TestRunner(object): 54 """Command line utility class for running pre-defined Android test(s).""" 55 56 _TEST_FILE_NAME = "test_defs.xml" 57 58 # file path to android core platform tests, relative to android build root 59 # TODO move these test data files to another directory 60 _CORE_TEST_PATH = os.path.join("development", "testrunner", 61 _TEST_FILE_NAME) 62 63 # vendor glob file path patterns to tests, relative to android 64 # build root 65 _VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo", 66 _TEST_FILE_NAME) 67 68 _RUNTEST_USAGE = ( 69 "usage: runtest.py [options] short-test-name[s]\n\n" 70 "The runtest script works in two ways. You can query it " 71 "for a list of tests, or you can launch one or more tests.") 72 73 # default value for make -jX 74 _DEFAULT_JOBS = 16 75 76 _DALVIK_VERIFIER_PROP = "dalvik.vm.dexopt-flags" 77 _DALVIK_VERIFIER_OFF_VALUE = "v=n" 78 _DALVIK_VERIFIER_OFF_PROP = "%s = %s" %(_DALVIK_VERIFIER_PROP, _DALVIK_VERIFIER_OFF_VALUE) 79 80 # regular expression to match path to artifacts to install in make output 81 _RE_MAKE_INSTALL = re.compile(r'INSTALL-PATH:\s([^\s]+)\s(.*)$') 82 83 def __init__(self): 84 # disable logging of timestamp 85 self._root_path = android_build.GetTop() 86 out_base_name = os.path.basename(android_build.GetOutDir()) 87 # regular expression to find remote device path from a file path relative 88 # to build root 89 pattern = r'' + out_base_name + r'\/target\/product\/\w+\/(.+)$' 90 self._re_make_install_path = re.compile(pattern) 91 logger.SetTimestampLogging(False) 92 self._adb = None 93 self._known_tests = None 94 self._options = None 95 self._test_args = None 96 self._tests_to_run = None 97 98 def _ProcessOptions(self): 99 """Processes command-line options.""" 100 # TODO error messages on once-only or mutually-exclusive options. 101 user_test_default = os.path.join(os.environ.get("HOME"), ".android", 102 self._TEST_FILE_NAME) 103 104 parser = optparse.OptionParser(usage=self._RUNTEST_USAGE) 105 106 parser.add_option("-l", "--list-tests", dest="only_list_tests", 107 default=False, action="store_true", 108 help="To view the list of tests") 109 parser.add_option("-b", "--skip-build", dest="skip_build", default=False, 110 action="store_true", help="Skip build - just launch") 111 parser.add_option("-j", "--jobs", dest="make_jobs", 112 metavar="X", default=self._DEFAULT_JOBS, 113 help="Number of make jobs to use when building") 114 parser.add_option("-n", "--skip_execute", dest="preview", default=False, 115 action="store_true", 116 help="Do not execute, just preview commands") 117 parser.add_option("-i", "--build-install-only", dest="build_install_only", default=False, 118 action="store_true", 119 help="Do not execute, build tests and install to device only") 120 parser.add_option("-r", "--raw-mode", dest="raw_mode", default=False, 121 action="store_true", 122 help="Raw mode (for output to other tools)") 123 parser.add_option("-a", "--suite-assign", dest="suite_assign_mode", 124 default=False, action="store_true", 125 help="Suite assignment (for details & usage see " 126 "InstrumentationTestRunner)") 127 parser.add_option("-v", "--verbose", dest="verbose", default=False, 128 action="store_true", 129 help="Increase verbosity of %s" % sys.argv[0]) 130 parser.add_option("-w", "--wait-for-debugger", dest="wait_for_debugger", 131 default=False, action="store_true", 132 help="Wait for debugger before launching tests") 133 parser.add_option("-c", "--test-class", dest="test_class", 134 help="Restrict test to a specific class") 135 parser.add_option("-m", "--test-method", dest="test_method", 136 help="Restrict test to a specific method") 137 parser.add_option("-p", "--test-package", dest="test_package", 138 help="Restrict test to a specific java package") 139 parser.add_option("-z", "--size", dest="test_size", 140 help="Restrict test to a specific test size") 141 parser.add_option("--annotation", dest="test_annotation", 142 help="Include only those tests tagged with a specific" 143 " annotation") 144 parser.add_option("--not-annotation", dest="test_not_annotation", 145 help="Exclude any tests tagged with a specific" 146 " annotation") 147 parser.add_option("-u", "--user-tests-file", dest="user_tests_file", 148 metavar="FILE", default=user_test_default, 149 help="Alternate source of user test definitions") 150 parser.add_option("-o", "--coverage", dest="coverage", 151 default=False, action="store_true", 152 help="Generate code coverage metrics for test(s)") 153 parser.add_option("--coverage-target", dest="coverage_target_path", 154 default=None, 155 help="Path to app to collect code coverage target data for.") 156 parser.add_option("-x", "--path", dest="test_path", 157 help="Run test(s) at given file system path") 158 parser.add_option("-t", "--all-tests", dest="all_tests", 159 default=False, action="store_true", 160 help="Run all defined tests") 161 parser.add_option("--continuous", dest="continuous_tests", 162 default=False, action="store_true", 163 help="Run all tests defined as part of the continuous " 164 "test set") 165 parser.add_option("--timeout", dest="timeout", 166 default=300, help="Set a timeout limit (in sec) for " 167 "running native tests on a device (default: 300 secs)") 168 parser.add_option("--suite", dest="suite", 169 help="Run all tests defined as part of the " 170 "the given test suite") 171 group = optparse.OptionGroup( 172 parser, "Targets", "Use these options to direct tests to a specific " 173 "Android target") 174 group.add_option("-e", "--emulator", dest="emulator", default=False, 175 action="store_true", help="use emulator") 176 group.add_option("-d", "--device", dest="device", default=False, 177 action="store_true", help="use device") 178 group.add_option("-s", "--serial", dest="serial", 179 help="use specific serial") 180 parser.add_option_group(group) 181 self._options, self._test_args = parser.parse_args() 182 183 if (not self._options.only_list_tests 184 and not self._options.all_tests 185 and not self._options.continuous_tests 186 and not self._options.suite 187 and not self._options.test_path 188 and len(self._test_args) < 1): 189 parser.print_help() 190 logger.SilentLog("at least one test name must be specified") 191 raise errors.AbortError 192 193 self._adb = adb_interface.AdbInterface() 194 if self._options.emulator: 195 self._adb.SetEmulatorTarget() 196 elif self._options.device: 197 self._adb.SetDeviceTarget() 198 elif self._options.serial is not None: 199 self._adb.SetTargetSerial(self._options.serial) 200 if self._options.verbose: 201 logger.SetVerbose(True) 202 203 if self._options.coverage_target_path: 204 self._options.coverage = True 205 206 self._known_tests = self._ReadTests() 207 208 self._options.host_lib_path = android_build.GetHostLibraryPath() 209 self._options.test_data_path = android_build.GetTestAppPath() 210 211 def _ReadTests(self): 212 """Parses the set of test definition data. 213 214 Returns: 215 A TestDefinitions object that contains the set of parsed tests. 216 Raises: 217 AbortError: If a fatal error occurred when parsing the tests. 218 """ 219 try: 220 known_tests = test_defs.TestDefinitions() 221 # only read tests when not in path mode 222 if not self._options.test_path: 223 core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH) 224 if os.path.isfile(core_test_path): 225 known_tests.Parse(core_test_path) 226 # read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths 227 vendor_tests_pattern = os.path.join(self._root_path, 228 self._VENDOR_TEST_PATH) 229 test_file_paths = glob.glob(vendor_tests_pattern) 230 for test_file_path in test_file_paths: 231 known_tests.Parse(test_file_path) 232 if os.path.isfile(self._options.user_tests_file): 233 known_tests.Parse(self._options.user_tests_file) 234 return known_tests 235 except errors.ParseError: 236 raise errors.AbortError 237 238 def _DumpTests(self): 239 """Prints out set of defined tests.""" 240 print "The following tests are currently defined:\n" 241 print "%-25s %-40s %s" % ("name", "build path", "description") 242 print "-" * 80 243 for test in self._known_tests: 244 print "%-25s %-40s %s" % (test.GetName(), test.GetBuildPath(), 245 test.GetDescription()) 246 print "\nSee %s for more information" % self._TEST_FILE_NAME 247 248 def _DoBuild(self): 249 logger.SilentLog("Building tests...") 250 251 tests = self._GetTestsToRun() 252 # turn off dalvik verifier if necessary 253 # TODO: skip turning off verifier for now, since it puts device in bad 254 # state b/14088982 255 #self._TurnOffVerifier(tests) 256 self._DoFullBuild(tests) 257 258 target_tree = make_tree.MakeTree() 259 260 extra_args_set = [] 261 for test_suite in tests: 262 self._AddBuildTarget(test_suite, target_tree, extra_args_set) 263 264 if not self._options.preview: 265 self._adb.EnableAdbRoot() 266 else: 267 logger.Log("adb root") 268 269 if not target_tree.IsEmpty(): 270 if self._options.coverage: 271 coverage.EnableCoverageBuild() 272 target_tree.AddPath("external/emma") 273 274 target_list = target_tree.GetPrunedMakeList() 275 target_dir_list = [re.sub(r'Android[.]mk$', r'', i) for i in target_list] 276 target_build_string = " ".join(target_list) 277 target_dir_build_string = " ".join(target_dir_list) 278 extra_args_string = " ".join(extra_args_set) 279 280 # mmm cannot be used from python, so perform a similar operation using 281 # ONE_SHOT_MAKEFILE 282 cmd = 'ONE_SHOT_MAKEFILE="%s" make -j%s -C "%s" GET-INSTALL-PATH all_modules %s' % ( 283 target_build_string, self._options.make_jobs, self._root_path, 284 extra_args_string) 285 # mmma equivalent, used when regular mmm fails 286 alt_cmd = 'make -j%s -C "%s" -f build/core/main.mk %s all_modules BUILD_MODULES_IN_PATHS="%s"' % ( 287 self._options.make_jobs, self._root_path, extra_args_string, target_dir_build_string) 288 289 logger.Log(cmd) 290 if not self._options.preview: 291 run_command.SetAbortOnError() 292 try: 293 output = run_command.RunCommand(cmd, return_output=True, timeout_time=600) 294 ## Chances are this failed because it didn't build the dependencies 295 except errors.AbortError: 296 logger.Log("make failed. Trying to rebuild all dependencies.") 297 logger.Log("mmma -j%s %s" %(self._options.make_jobs, target_dir_build_string)) 298 # Try again with mma equivalent, which will build the dependencies 299 run_command.RunCommand(alt_cmd, return_output=False, timeout_time=600) 300 # Run mmm again to get the install paths only 301 output = run_command.RunCommand(cmd, return_output=True, timeout_time=600) 302 run_command.SetAbortOnError(False) 303 logger.SilentLog(output) 304 self._DoInstall(output) 305 306 def _DoInstall(self, make_output): 307 """Install artifacts from build onto device. 308 309 Looks for 'install:' text from make output to find artifacts to install. 310 311 Files with the .apk extension get 'adb install'ed, all other files 312 get 'adb push'ed onto the device. 313 314 Args: 315 make_output: stdout from make command 316 """ 317 for line in make_output.split("\n"): 318 m = self._RE_MAKE_INSTALL.match(line) 319 if m: 320 # strip the 'INSTALL: <name>' from the left hand side 321 # the remaining string is a space-separated list of build-generated files 322 install_paths = m.group(2) 323 for install_path in re.split(r'\s+', install_paths): 324 if install_path.endswith(".apk"): 325 abs_install_path = os.path.join(self._root_path, install_path) 326 logger.Log("adb install -r %s" % abs_install_path) 327 logger.Log(self._adb.Install(abs_install_path)) 328 else: 329 self._PushInstallFileToDevice(install_path) 330 331 def _PushInstallFileToDevice(self, install_path): 332 m = self._re_make_install_path.match(install_path) 333 if m: 334 remote_path = m.group(1) 335 remote_dir = os.path.dirname(remote_path) 336 logger.Log("adb shell mkdir -p %s" % remote_dir) 337 self._adb.SendShellCommand("mkdir -p %s" % remote_dir) 338 abs_install_path = os.path.join(self._root_path, install_path) 339 logger.Log("adb push %s %s" % (abs_install_path, remote_path)) 340 self._adb.Push(abs_install_path, remote_path) 341 else: 342 logger.Log("Error: Failed to recognize path of file to install %s" % install_path) 343 344 def _DoFullBuild(self, tests): 345 """If necessary, run a full 'make' command for the tests that need it.""" 346 extra_args_set = Set() 347 348 for test in tests: 349 if test.IsFullMake(): 350 if test.GetExtraBuildArgs(): 351 # extra args contains the args to pass to 'make' 352 extra_args_set.add(test.GetExtraBuildArgs()) 353 else: 354 logger.Log("Warning: test %s needs a full build but does not specify" 355 " extra_build_args" % test.GetName()) 356 357 # check if there is actually any tests that required a full build 358 if extra_args_set: 359 cmd = ('make -j%s %s' % (self._options.make_jobs, 360 ' '.join(list(extra_args_set)))) 361 logger.Log(cmd) 362 if not self._options.preview: 363 old_dir = os.getcwd() 364 os.chdir(self._root_path) 365 output = run_command.RunCommand(cmd, return_output=True) 366 logger.SilentLog(output) 367 os.chdir(old_dir) 368 self._DoInstall(output) 369 370 def _AddBuildTarget(self, test_suite, target_tree, extra_args_set): 371 if not test_suite.IsFullMake(): 372 build_dir = test_suite.GetBuildPath() 373 if self._AddBuildTargetPath(build_dir, target_tree): 374 extra_args_set.append(test_suite.GetExtraBuildArgs()) 375 for path in test_suite.GetBuildDependencies(self._options): 376 self._AddBuildTargetPath(path, target_tree) 377 378 def _AddBuildTargetPath(self, build_dir, target_tree): 379 if build_dir is not None: 380 target_tree.AddPath(build_dir) 381 return True 382 return False 383 384 def _GetTestsToRun(self): 385 """Get a list of TestSuite objects to run, based on command line args.""" 386 if self._tests_to_run: 387 return self._tests_to_run 388 389 self._tests_to_run = [] 390 if self._options.all_tests: 391 self._tests_to_run = self._known_tests.GetTests() 392 elif self._options.continuous_tests: 393 self._tests_to_run = self._known_tests.GetContinuousTests() 394 elif self._options.suite: 395 self._tests_to_run = \ 396 self._known_tests.GetTestsInSuite(self._options.suite) 397 elif self._options.test_path: 398 walker = test_walker.TestWalker() 399 self._tests_to_run = walker.FindTests(self._options.test_path) 400 401 for name in self._test_args: 402 test = self._known_tests.GetTest(name) 403 if test is None: 404 logger.Log("Error: Could not find test %s" % name) 405 self._DumpTests() 406 raise errors.AbortError 407 self._tests_to_run.append(test) 408 return self._tests_to_run 409 410 def _IsCtsTests(self, test_list): 411 """Check if any cts tests are included in given list of tests to run.""" 412 for test in test_list: 413 if test.GetSuite() == 'cts': 414 return True 415 return False 416 417 def _TurnOffVerifier(self, test_list): 418 """Turn off the dalvik verifier if needed by given tests. 419 420 If one or more tests needs dalvik verifier off, and it is not already off, 421 turns off verifier and reboots device to allow change to take effect. 422 """ 423 # hack to check if these are frameworks/base tests. If so, turn off verifier 424 # to allow framework tests to access private/protected/package-private framework api 425 framework_test = False 426 for test in test_list: 427 if os.path.commonprefix([test.GetBuildPath(), "frameworks/base"]): 428 framework_test = True 429 if framework_test: 430 # check if verifier is off already - to avoid the reboot if not 431 # necessary 432 output = self._adb.SendShellCommand("cat /data/local.prop") 433 if not self._DALVIK_VERIFIER_OFF_PROP in output: 434 435 # Read the existing dalvik verifier flags. 436 old_prop_value = self._adb.SendShellCommand("getprop %s" \ 437 %(self._DALVIK_VERIFIER_PROP)) 438 old_prop_value = old_prop_value.strip() if old_prop_value else "" 439 440 # Append our verifier flags to existing flags 441 new_prop_value = "%s %s" %(self._DALVIK_VERIFIER_OFF_VALUE, old_prop_value) 442 443 # Update property now, as /data/local.prop is not read until reboot 444 logger.Log("adb shell setprop %s '%s'" \ 445 %(self._DALVIK_VERIFIER_PROP, new_prop_value)) 446 if not self._options.preview: 447 self._adb.SendShellCommand("setprop %s '%s'" \ 448 %(self._DALVIK_VERIFIER_PROP, new_prop_value)) 449 450 # Write prop to /data/local.prop 451 # Every time device is booted, it will pick up this value 452 new_prop_assignment = "%s = %s" %(self._DALVIK_VERIFIER_PROP, new_prop_value) 453 if self._options.preview: 454 logger.Log("adb shell \"echo %s >> /data/local.prop\"" 455 % new_prop_assignment) 456 logger.Log("adb shell chmod 644 /data/local.prop") 457 else: 458 logger.Log("Turning off dalvik verifier and rebooting") 459 self._adb.SendShellCommand("\"echo %s >> /data/local.prop\"" 460 % new_prop_assignment) 461 462 # Reset runtime so that dalvik picks up new verifier flags from prop 463 self._ChmodRuntimeReset() 464 elif not self._options.preview: 465 # check the permissions on the file 466 permout = self._adb.SendShellCommand("ls -l /data/local.prop") 467 if not "-rw-r--r--" in permout: 468 logger.Log("Fixing permissions on /data/local.prop and rebooting") 469 self._ChmodRuntimeReset() 470 471 def _ChmodRuntimeReset(self): 472 """Perform a chmod of /data/local.prop and reset the runtime. 473 """ 474 logger.Log("adb shell chmod 644 /data/local.prop ## u+w,a+r") 475 if not self._options.preview: 476 self._adb.SendShellCommand("chmod 644 /data/local.prop") 477 478 self._adb.RuntimeReset(preview_only=self._options.preview) 479 480 if not self._options.preview: 481 self._adb.EnableAdbRoot() 482 483 484 def RunTests(self): 485 """Main entry method - executes the tests according to command line args.""" 486 try: 487 run_command.SetAbortOnError() 488 self._ProcessOptions() 489 if self._options.only_list_tests: 490 self._DumpTests() 491 return 492 493 if not self._options.skip_build: 494 self._DoBuild() 495 496 if self._options.build_install_only: 497 logger.Log("Skipping test execution (due to --build-install-only flag)") 498 return 499 500 for test_suite in self._GetTestsToRun(): 501 try: 502 test_suite.Run(self._options, self._adb) 503 except errors.WaitForResponseTimedOutError: 504 logger.Log("Timed out waiting for response") 505 506 except KeyboardInterrupt: 507 logger.Log("Exiting...") 508 except errors.AbortError, error: 509 logger.Log(error.msg) 510 logger.SilentLog("Exiting due to AbortError...") 511 except errors.WaitForResponseTimedOutError: 512 logger.Log("Timed out waiting for response") 513 514 515 def RunTests(): 516 runner = TestRunner() 517 runner.RunTests() 518 519 if __name__ == "__main__": 520 RunTests() 521