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   def __init__(self):
     74     # disable logging of timestamp
     75     self._root_path = android_build.GetTop()
     76     logger.SetTimestampLogging(False)
     77     self._adb = None
     78     self._known_tests = None
     79     self._options = None
     80     self._test_args = None
     81     self._tests_to_run = None
     82 
     83   def _ProcessOptions(self):
     84     """Processes command-line options."""
     85     # TODO error messages on once-only or mutually-exclusive options.
     86     user_test_default = os.path.join(os.environ.get("HOME"), ".android",
     87                                      self._TEST_FILE_NAME)
     88 
     89     parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
     90 
     91     parser.add_option("-l", "--list-tests", dest="only_list_tests",
     92                       default=False, action="store_true",
     93                       help="To view the list of tests")
     94     parser.add_option("-b", "--skip-build", dest="skip_build", default=False,
     95                       action="store_true", help="Skip build - just launch")
     96     parser.add_option("-j", "--jobs", dest="make_jobs",
     97                       metavar="X", default=self._DEFAULT_JOBS,
     98                       help="Number of make jobs to use when building")
     99     parser.add_option("-n", "--skip_execute", dest="preview", default=False,
    100                       action="store_true",
    101                       help="Do not execute, just preview commands")
    102     parser.add_option("-r", "--raw-mode", dest="raw_mode", default=False,
    103                       action="store_true",
    104                       help="Raw mode (for output to other tools)")
    105     parser.add_option("-a", "--suite-assign", dest="suite_assign_mode",
    106                       default=False, action="store_true",
    107                       help="Suite assignment (for details & usage see "
    108                       "InstrumentationTestRunner)")
    109     parser.add_option("-v", "--verbose", dest="verbose", default=False,
    110                       action="store_true",
    111                       help="Increase verbosity of %s" % sys.argv[0])
    112     parser.add_option("-w", "--wait-for-debugger", dest="wait_for_debugger",
    113                       default=False, action="store_true",
    114                       help="Wait for debugger before launching tests")
    115     parser.add_option("-c", "--test-class", dest="test_class",
    116                       help="Restrict test to a specific class")
    117     parser.add_option("-m", "--test-method", dest="test_method",
    118                       help="Restrict test to a specific method")
    119     parser.add_option("-p", "--test-package", dest="test_package",
    120                       help="Restrict test to a specific java package")
    121     parser.add_option("-z", "--size", dest="test_size",
    122                       help="Restrict test to a specific test size")
    123     parser.add_option("--annotation", dest="test_annotation",
    124                       help="Include only those tests tagged with a specific"
    125                       " annotation")
    126     parser.add_option("--not-annotation", dest="test_not_annotation",
    127                       help="Exclude any tests tagged with a specific"
    128                       " annotation")
    129     parser.add_option("-u", "--user-tests-file", dest="user_tests_file",
    130                       metavar="FILE", default=user_test_default,
    131                       help="Alternate source of user test definitions")
    132     parser.add_option("-o", "--coverage", dest="coverage",
    133                       default=False, action="store_true",
    134                       help="Generate code coverage metrics for test(s)")
    135     parser.add_option("-x", "--path", dest="test_path",
    136                       help="Run test(s) at given file system path")
    137     parser.add_option("-t", "--all-tests", dest="all_tests",
    138                       default=False, action="store_true",
    139                       help="Run all defined tests")
    140     parser.add_option("--continuous", dest="continuous_tests",
    141                       default=False, action="store_true",
    142                       help="Run all tests defined as part of the continuous "
    143                       "test set")
    144     parser.add_option("--timeout", dest="timeout",
    145                       default=300, help="Set a timeout limit (in sec) for "
    146                       "running native tests on a device (default: 300 secs)")
    147     parser.add_option("--suite", dest="suite",
    148                       help="Run all tests defined as part of the "
    149                       "the given test suite")
    150     group = optparse.OptionGroup(
    151         parser, "Targets", "Use these options to direct tests to a specific "
    152         "Android target")
    153     group.add_option("-e", "--emulator", dest="emulator", default=False,
    154                      action="store_true", help="use emulator")
    155     group.add_option("-d", "--device", dest="device", default=False,
    156                      action="store_true", help="use device")
    157     group.add_option("-s", "--serial", dest="serial",
    158                      help="use specific serial")
    159     parser.add_option_group(group)
    160 
    161     self._options, self._test_args = parser.parse_args()
    162 
    163     if (not self._options.only_list_tests
    164         and not self._options.all_tests
    165         and not self._options.continuous_tests
    166         and not self._options.suite
    167         and not self._options.test_path
    168         and len(self._test_args) < 1):
    169       parser.print_help()
    170       logger.SilentLog("at least one test name must be specified")
    171       raise errors.AbortError
    172 
    173     self._adb = adb_interface.AdbInterface()
    174     if self._options.emulator:
    175       self._adb.SetEmulatorTarget()
    176     elif self._options.device:
    177       self._adb.SetDeviceTarget()
    178     elif self._options.serial is not None:
    179       self._adb.SetTargetSerial(self._options.serial)
    180 
    181     if self._options.verbose:
    182       logger.SetVerbose(True)
    183 
    184     self._known_tests = self._ReadTests()
    185 
    186     self._options.host_lib_path = android_build.GetHostLibraryPath()
    187     self._options.test_data_path = android_build.GetTestAppPath()
    188 
    189   def _ReadTests(self):
    190     """Parses the set of test definition data.
    191 
    192     Returns:
    193       A TestDefinitions object that contains the set of parsed tests.
    194     Raises:
    195       AbortError: If a fatal error occurred when parsing the tests.
    196     """
    197     core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
    198     try:
    199       known_tests = test_defs.TestDefinitions()
    200       known_tests.Parse(core_test_path)
    201       # read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
    202       vendor_tests_pattern = os.path.join(self._root_path,
    203                                           self._VENDOR_TEST_PATH)
    204       test_file_paths = glob.glob(vendor_tests_pattern)
    205       for test_file_path in test_file_paths:
    206         known_tests.Parse(test_file_path)
    207       if os.path.isfile(self._options.user_tests_file):
    208         known_tests.Parse(self._options.user_tests_file)
    209       return known_tests
    210     except errors.ParseError:
    211       raise errors.AbortError
    212 
    213   def _DumpTests(self):
    214     """Prints out set of defined tests."""
    215     print "The following tests are currently defined:\n"
    216     print "%-25s %-40s %s" % ("name", "build path", "description")
    217     print "-" * 80
    218     for test in self._known_tests:
    219       print "%-25s %-40s %s" % (test.GetName(), test.GetBuildPath(),
    220                                 test.GetDescription())
    221     print "\nSee %s for more information" % self._TEST_FILE_NAME
    222 
    223   def _DoBuild(self):
    224     logger.SilentLog("Building tests...")
    225     target_set = Set()
    226     extra_args_set = Set()
    227     tests = self._GetTestsToRun()
    228     for test_suite in tests:
    229       self._AddBuildTarget(test_suite, target_set, extra_args_set)
    230 
    231     if target_set:
    232       if self._options.coverage:
    233         coverage.EnableCoverageBuild()
    234 
    235       # hack to build cts dependencies
    236       # TODO: remove this when build dependency support added to runtest or
    237       # cts dependencies are removed
    238       if self._IsCtsTests(tests):
    239         # need to use make since these fail building with ONE_SHOT_MAKEFILE
    240         cmd = ('make -j%s CtsTestStubs android.core.tests.runner' %
    241                self._options.make_jobs)
    242         logger.Log(cmd)
    243         if not self._options.preview:
    244           old_dir = os.getcwd()
    245           os.chdir(self._root_path)
    246           run_command.RunCommand(cmd, return_output=False)
    247           os.chdir(old_dir)
    248       target_build_string = " ".join(list(target_set))
    249       extra_args_string = " ".join(list(extra_args_set))
    250       # mmm cannot be used from python, so perform a similar operation using
    251       # ONE_SHOT_MAKEFILE
    252       cmd = 'ONE_SHOT_MAKEFILE="%s" make -j%s -C "%s" files %s' % (
    253           target_build_string, self._options.make_jobs, self._root_path,
    254           extra_args_string)
    255       logger.Log(cmd)
    256 
    257       if self._options.preview:
    258         # in preview mode, just display to the user what command would have been
    259         # run
    260         logger.Log("adb sync")
    261       else:
    262         run_command.RunCommand(cmd, return_output=False)
    263         logger.Log("Syncing to device...")
    264         self._adb.Sync()
    265 
    266   def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
    267     build_dir = test_suite.GetBuildPath()
    268     if self._AddBuildTargetPath(build_dir, target_set):
    269       extra_args_set.add(test_suite.GetExtraBuildArgs())
    270     for path in test_suite.GetBuildDependencies(self._options):
    271       self._AddBuildTargetPath(path, target_set)
    272 
    273   def _AddBuildTargetPath(self, build_dir, target_set):
    274     if build_dir is not None:
    275       build_file_path = os.path.join(build_dir, "Android.mk")
    276       if os.path.isfile(os.path.join(self._root_path, build_file_path)):
    277         target_set.add(build_file_path)
    278         return True
    279       else:
    280         logger.Log("%s has no Android.mk, skipping" % build_dir)
    281     return False
    282 
    283   def _GetTestsToRun(self):
    284     """Get a list of TestSuite objects to run, based on command line args."""
    285     if self._tests_to_run:
    286       return self._tests_to_run
    287 
    288     self._tests_to_run = []
    289     if self._options.all_tests:
    290       self._tests_to_run = self._known_tests.GetTests()
    291     elif self._options.continuous_tests:
    292       self._tests_to_run = self._known_tests.GetContinuousTests()
    293     elif self._options.suite:
    294       self._tests_to_run = \
    295           self._known_tests.GetTestsInSuite(self._options.suite)
    296     elif self._options.test_path:
    297       walker = test_walker.TestWalker()
    298       self._tests_to_run = walker.FindTests(self._options.test_path)
    299 
    300     for name in self._test_args:
    301       test = self._known_tests.GetTest(name)
    302       if test is None:
    303         logger.Log("Error: Could not find test %s" % name)
    304         self._DumpTests()
    305         raise errors.AbortError
    306       self._tests_to_run.append(test)
    307     return self._tests_to_run
    308 
    309   def _IsCtsTests(self, test_list):
    310     """Check if any cts tests are included in given list of tests to run."""
    311     for test in test_list:
    312       if test.GetSuite() == 'cts':
    313         return True
    314     return False
    315 
    316   def RunTests(self):
    317     """Main entry method - executes the tests according to command line args."""
    318     try:
    319       run_command.SetAbortOnError()
    320       self._ProcessOptions()
    321       if self._options.only_list_tests:
    322         self._DumpTests()
    323         return
    324 
    325       if not self._options.skip_build:
    326         self._DoBuild()
    327 
    328       for test_suite in self._GetTestsToRun():
    329         try:
    330           test_suite.Run(self._options, self._adb)
    331         except errors.WaitForResponseTimedOutError:
    332           logger.Log("Timed out waiting for response")
    333 
    334     except KeyboardInterrupt:
    335       logger.Log("Exiting...")
    336     except errors.AbortError, error:
    337       logger.Log(error.msg)
    338       logger.SilentLog("Exiting due to AbortError...")
    339     except errors.WaitForResponseTimedOutError:
    340       logger.Log("Timed out waiting for response")
    341 
    342 
    343 def RunTests():
    344   runner = TestRunner()
    345   runner.RunTests()
    346 
    347 if __name__ == "__main__":
    348   RunTests()
    349