Home | History | Annotate | Download | only in testrunner
      1 #!/usr/bin/python2.4
      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 from sets import Set
     37 import sys
     38 
     39 # local imports
     40 import adb_interface
     41 import android_build
     42 import coverage
     43 import errors
     44 import logger
     45 import run_command
     46 from test_defs import test_defs
     47 from test_defs import test_walker
     48 
     49 
     50 class TestRunner(object):
     51   """Command line utility class for running pre-defined Android test(s)."""
     52 
     53   _TEST_FILE_NAME = "test_defs.xml"
     54 
     55   # file path to android core platform tests, relative to android build root
     56   # TODO move these test data files to another directory
     57   _CORE_TEST_PATH = os.path.join("development", "testrunner",
     58                                  _TEST_FILE_NAME)
     59 
     60   # vendor glob file path patterns to tests, relative to android
     61   # build root
     62   _VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
     63                                    _TEST_FILE_NAME)
     64 
     65   _RUNTEST_USAGE = (
     66       "usage: runtest.py [options] short-test-name[s]\n\n"
     67       "The runtest script works in two ways.  You can query it "
     68       "for a list of tests, or you can launch one or more tests.")
     69 
     70   # default value for make -jX
     71   _DEFAULT_JOBS = 4
     72 
     73   _DALVIK_VERIFIER_OFF_PROP = "dalvik.vm.dexopt-flags = v=n"
     74 
     75   def __init__(self):
     76     # disable logging of timestamp
     77     self._root_path = android_build.GetTop()
     78     logger.SetTimestampLogging(False)
     79     self._adb = None
     80     self._known_tests = None
     81     self._options = None
     82     self._test_args = None
     83     self._tests_to_run = None
     84 
     85   def _ProcessOptions(self):
     86     """Processes command-line options."""
     87     # TODO error messages on once-only or mutually-exclusive options.
     88     user_test_default = os.path.join(os.environ.get("HOME"), ".android",
     89                                      self._TEST_FILE_NAME)
     90 
     91     parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
     92 
     93     parser.add_option("-l", "--list-tests", dest="only_list_tests",
     94                       default=False, action="store_true",
     95                       help="To view the list of tests")
     96     parser.add_option("-b", "--skip-build", dest="skip_build", default=False,
     97                       action="store_true", help="Skip build - just launch")
     98     parser.add_option("-j", "--jobs", dest="make_jobs",
     99                       metavar="X", default=self._DEFAULT_JOBS,
    100                       help="Number of make jobs to use when building")
    101     parser.add_option("-n", "--skip_execute", dest="preview", default=False,
    102                       action="store_true",
    103                       help="Do not execute, just preview commands")
    104     parser.add_option("-r", "--raw-mode", dest="raw_mode", default=False,
    105                       action="store_true",
    106                       help="Raw mode (for output to other tools)")
    107     parser.add_option("-a", "--suite-assign", dest="suite_assign_mode",
    108                       default=False, action="store_true",
    109                       help="Suite assignment (for details & usage see "
    110                       "InstrumentationTestRunner)")
    111     parser.add_option("-v", "--verbose", dest="verbose", default=False,
    112                       action="store_true",
    113                       help="Increase verbosity of %s" % sys.argv[0])
    114     parser.add_option("-w", "--wait-for-debugger", dest="wait_for_debugger",
    115                       default=False, action="store_true",
    116                       help="Wait for debugger before launching tests")
    117     parser.add_option("-c", "--test-class", dest="test_class",
    118                       help="Restrict test to a specific class")
    119     parser.add_option("-m", "--test-method", dest="test_method",
    120                       help="Restrict test to a specific method")
    121     parser.add_option("-p", "--test-package", dest="test_package",
    122                       help="Restrict test to a specific java package")
    123     parser.add_option("-z", "--size", dest="test_size",
    124                       help="Restrict test to a specific test size")
    125     parser.add_option("--annotation", dest="test_annotation",
    126                       help="Include only those tests tagged with a specific"
    127                       " annotation")
    128     parser.add_option("--not-annotation", dest="test_not_annotation",
    129                       help="Exclude any tests tagged with a specific"
    130                       " annotation")
    131     parser.add_option("-u", "--user-tests-file", dest="user_tests_file",
    132                       metavar="FILE", default=user_test_default,
    133                       help="Alternate source of user test definitions")
    134     parser.add_option("-o", "--coverage", dest="coverage",
    135                       default=False, action="store_true",
    136                       help="Generate code coverage metrics for test(s)")
    137     parser.add_option("-x", "--path", dest="test_path",
    138                       help="Run test(s) at given file system path")
    139     parser.add_option("-t", "--all-tests", dest="all_tests",
    140                       default=False, action="store_true",
    141                       help="Run all defined tests")
    142     parser.add_option("--continuous", dest="continuous_tests",
    143                       default=False, action="store_true",
    144                       help="Run all tests defined as part of the continuous "
    145                       "test set")
    146     parser.add_option("--timeout", dest="timeout",
    147                       default=300, help="Set a timeout limit (in sec) for "
    148                       "running native tests on a device (default: 300 secs)")
    149     parser.add_option("--suite", dest="suite",
    150                       help="Run all tests defined as part of the "
    151                       "the given test suite")
    152     group = optparse.OptionGroup(
    153         parser, "Targets", "Use these options to direct tests to a specific "
    154         "Android target")
    155     group.add_option("-e", "--emulator", dest="emulator", default=False,
    156                      action="store_true", help="use emulator")
    157     group.add_option("-d", "--device", dest="device", default=False,
    158                      action="store_true", help="use device")
    159     group.add_option("-s", "--serial", dest="serial",
    160                      help="use specific serial")
    161     parser.add_option_group(group)
    162     self._options, self._test_args = parser.parse_args()
    163 
    164     if (not self._options.only_list_tests
    165         and not self._options.all_tests
    166         and not self._options.continuous_tests
    167         and not self._options.suite
    168         and not self._options.test_path
    169         and len(self._test_args) < 1):
    170       parser.print_help()
    171       logger.SilentLog("at least one test name must be specified")
    172       raise errors.AbortError
    173 
    174     self._adb = adb_interface.AdbInterface()
    175     if self._options.emulator:
    176       self._adb.SetEmulatorTarget()
    177     elif self._options.device:
    178       self._adb.SetDeviceTarget()
    179     elif self._options.serial is not None:
    180       self._adb.SetTargetSerial(self._options.serial)
    181 
    182     if self._options.verbose:
    183       logger.SetVerbose(True)
    184 
    185     self._known_tests = self._ReadTests()
    186 
    187     self._options.host_lib_path = android_build.GetHostLibraryPath()
    188     self._options.test_data_path = android_build.GetTestAppPath()
    189 
    190   def _ReadTests(self):
    191     """Parses the set of test definition data.
    192 
    193     Returns:
    194       A TestDefinitions object that contains the set of parsed tests.
    195     Raises:
    196       AbortError: If a fatal error occurred when parsing the tests.
    197     """
    198     core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
    199     try:
    200       known_tests = test_defs.TestDefinitions()
    201       known_tests.Parse(core_test_path)
    202       # read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
    203       vendor_tests_pattern = os.path.join(self._root_path,
    204                                           self._VENDOR_TEST_PATH)
    205       test_file_paths = glob.glob(vendor_tests_pattern)
    206       for test_file_path in test_file_paths:
    207         known_tests.Parse(test_file_path)
    208       if os.path.isfile(self._options.user_tests_file):
    209         known_tests.Parse(self._options.user_tests_file)
    210       return known_tests
    211     except errors.ParseError:
    212       raise errors.AbortError
    213 
    214   def _DumpTests(self):
    215     """Prints out set of defined tests."""
    216     print "The following tests are currently defined:\n"
    217     print "%-25s %-40s %s" % ("name", "build path", "description")
    218     print "-" * 80
    219     for test in self._known_tests:
    220       print "%-25s %-40s %s" % (test.GetName(), test.GetBuildPath(),
    221                                 test.GetDescription())
    222     print "\nSee %s for more information" % self._TEST_FILE_NAME
    223 
    224   def _DoBuild(self):
    225     logger.SilentLog("Building tests...")
    226     target_set = Set()
    227     extra_args_set = Set()
    228     tests = self._GetTestsToRun()
    229     for test_suite in tests:
    230       self._AddBuildTarget(test_suite, target_set, extra_args_set)
    231 
    232     if not self._options.preview:
    233       self._adb.EnableAdbRoot()
    234     else:
    235       logger.Log("adb root")
    236     rebuild_libcore = False
    237     if target_set:
    238       if self._options.coverage:
    239         coverage.EnableCoverageBuild()
    240         # hack to remove core library intermediates
    241         # hack is needed because:
    242         # 1. EMMA_INSTRUMENT changes what source files to include in libcore
    243         #    but it does not trigger a rebuild
    244         # 2. there's no target (like "clear-intermediates") to remove the files
    245         #    decently
    246         rebuild_libcore = not coverage.TestDeviceCoverageSupport(self._adb)
    247         if rebuild_libcore:
    248           cmd = "rm -rf %s" % os.path.join(
    249               self._root_path,
    250               "out/target/common/obj/JAVA_LIBRARIES/core_intermediates/")
    251           logger.Log(cmd)
    252           run_command.RunCommand(cmd, return_output=False)
    253 
    254       # hack to build cts dependencies
    255       # TODO: remove this when build dependency support added to runtest or
    256       # cts dependencies are removed
    257       if self._IsCtsTests(tests):
    258         # need to use make since these fail building with ONE_SHOT_MAKEFILE
    259         cmd = ('make -j%s CtsTestStubs android.core.tests.runner' %
    260                self._options.make_jobs)
    261         logger.Log(cmd)
    262         if not self._options.preview:
    263           old_dir = os.getcwd()
    264           os.chdir(self._root_path)
    265           run_command.RunCommand(cmd, return_output=False)
    266           os.chdir(old_dir)
    267       # turn off dalvik verifier if necessary
    268       self._TurnOffVerifier(tests)
    269       target_build_string = " ".join(list(target_set))
    270       extra_args_string = " ".join(list(extra_args_set))
    271       # mmm cannot be used from python, so perform a similar operation using
    272       # ONE_SHOT_MAKEFILE
    273       cmd = 'ONE_SHOT_MAKEFILE="%s" make -j%s -C "%s" files %s' % (
    274           target_build_string, self._options.make_jobs, self._root_path,
    275           extra_args_string)
    276       logger.Log(cmd)
    277 
    278       if self._options.preview:
    279         # in preview mode, just display to the user what command would have been
    280         # run
    281         logger.Log("adb sync")
    282       else:
    283         # set timeout for build to 10 minutes, since libcore may need to
    284         # be rebuilt
    285         run_command.RunCommand(cmd, return_output=False, timeout_time=600)
    286         logger.Log("Syncing to device...")
    287         self._adb.Sync(runtime_restart=rebuild_libcore)
    288 
    289   def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
    290     build_dir = test_suite.GetBuildPath()
    291     if self._AddBuildTargetPath(build_dir, target_set):
    292       extra_args_set.add(test_suite.GetExtraBuildArgs())
    293     for path in test_suite.GetBuildDependencies(self._options):
    294       self._AddBuildTargetPath(path, target_set)
    295 
    296   def _AddBuildTargetPath(self, build_dir, target_set):
    297     if build_dir is not None:
    298       build_file_path = os.path.join(build_dir, "Android.mk")
    299       if os.path.isfile(os.path.join(self._root_path, build_file_path)):
    300         target_set.add(build_file_path)
    301         return True
    302       else:
    303         logger.Log("%s has no Android.mk, skipping" % build_dir)
    304     return False
    305 
    306   def _GetTestsToRun(self):
    307     """Get a list of TestSuite objects to run, based on command line args."""
    308     if self._tests_to_run:
    309       return self._tests_to_run
    310 
    311     self._tests_to_run = []
    312     if self._options.all_tests:
    313       self._tests_to_run = self._known_tests.GetTests()
    314     elif self._options.continuous_tests:
    315       self._tests_to_run = self._known_tests.GetContinuousTests()
    316     elif self._options.suite:
    317       self._tests_to_run = \
    318           self._known_tests.GetTestsInSuite(self._options.suite)
    319     elif self._options.test_path:
    320       walker = test_walker.TestWalker()
    321       self._tests_to_run = walker.FindTests(self._options.test_path)
    322 
    323     for name in self._test_args:
    324       test = self._known_tests.GetTest(name)
    325       if test is None:
    326         logger.Log("Error: Could not find test %s" % name)
    327         self._DumpTests()
    328         raise errors.AbortError
    329       self._tests_to_run.append(test)
    330     return self._tests_to_run
    331 
    332   def _IsCtsTests(self, test_list):
    333     """Check if any cts tests are included in given list of tests to run."""
    334     for test in test_list:
    335       if test.GetSuite() == 'cts':
    336         return True
    337     return False
    338 
    339   def _TurnOffVerifier(self, test_list):
    340     """Turn off the dalvik verifier if needed by given tests.
    341 
    342     If one or more tests needs dalvik verifier off, and it is not already off,
    343     turns off verifier and reboots device to allow change to take effect.
    344     """
    345     # hack to check if these are framework/base tests. If so, turn off verifier
    346     # to allow framework tests to access package-private framework api
    347     framework_test = False
    348     for test in test_list:
    349       if os.path.commonprefix([test.GetBuildPath(), "frameworks/base"]):
    350         framework_test = True
    351     if framework_test:
    352       # check if verifier is off already - to avoid the reboot if not
    353       # necessary
    354       output = self._adb.SendShellCommand("cat /data/local.prop")
    355       if not self._DALVIK_VERIFIER_OFF_PROP in output:
    356         if self._options.preview:
    357           logger.Log("adb shell \"echo %s >> /data/local.prop\""
    358                      % self._DALVIK_VERIFIER_OFF_PROP)
    359           logger.Log("adb reboot")
    360           logger.Log("adb wait-for-device")
    361         else:
    362           logger.Log("Turning off dalvik verifier and rebooting")
    363           self._adb.SendShellCommand("\"echo %s >> /data/local.prop\""
    364                                      % self._DALVIK_VERIFIER_OFF_PROP)
    365           self._adb.SendCommand("reboot")
    366           self._adb.SendCommand("wait-for-device", timeout_time=60,
    367                                 retry_count=3)
    368 
    369   def RunTests(self):
    370     """Main entry method - executes the tests according to command line args."""
    371     try:
    372       run_command.SetAbortOnError()
    373       self._ProcessOptions()
    374       if self._options.only_list_tests:
    375         self._DumpTests()
    376         return
    377 
    378       if not self._options.skip_build:
    379         self._DoBuild()
    380 
    381       for test_suite in self._GetTestsToRun():
    382         try:
    383           test_suite.Run(self._options, self._adb)
    384         except errors.WaitForResponseTimedOutError:
    385           logger.Log("Timed out waiting for response")
    386 
    387     except KeyboardInterrupt:
    388       logger.Log("Exiting...")
    389     except errors.AbortError, error:
    390       logger.Log(error.msg)
    391       logger.SilentLog("Exiting due to AbortError...")
    392     except errors.WaitForResponseTimedOutError:
    393       logger.Log("Timed out waiting for response")
    394 
    395 
    396 def RunTests():
    397   runner = TestRunner()
    398   runner.RunTests()
    399 
    400 if __name__ == "__main__":
    401   RunTests()
    402