• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19runtest 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
23specify.
24
25runtest supports running tests whose attributes have been pre-defined in
26_TEST_FILE_NAME files, (runtest <testname>), or by specifying the file
27system path to the test to run (runtest --path <path>).
28
29Do runtest --help to see full list of options.
30"""
31
32# Python imports
33import glob
34import optparse
35import os
36from sets import Set
37import sys
38import time
39
40# local imports
41import adb_interface
42import android_build
43import coverage
44import errors
45import logger
46import run_command
47from test_defs import test_defs
48from test_defs import test_walker
49
50
51class 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
425def RunTests():
426  runner = TestRunner()
427  runner.RunTests()
428
429if __name__ == "__main__":
430  RunTests()
431