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