With this change, also refactored runtest as follows: Modified the test suite schema and python implementation to have an inheritance structure. Each test type has its own python module, which will also handle the logic of running the test.
280 lines
11 KiB
Python
Executable File
280 lines
11 KiB
Python
Executable File
#!/usr/bin/python2.4
|
|
#
|
|
# Copyright 2008, The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Command line utility for running a pre-defined test.
|
|
|
|
Based on previous <androidroot>/development/tools/runtest shell script.
|
|
"""
|
|
|
|
# Python imports
|
|
import glob
|
|
import optparse
|
|
import os
|
|
import re
|
|
from sets import Set
|
|
import sys
|
|
|
|
# local imports
|
|
import adb_interface
|
|
import android_build
|
|
import coverage
|
|
import errors
|
|
import logger
|
|
import run_command
|
|
from test_defs import test_defs
|
|
|
|
|
|
class TestRunner(object):
|
|
"""Command line utility class for running pre-defined Android test(s)."""
|
|
|
|
_TEST_FILE_NAME = "test_defs.xml"
|
|
|
|
# file path to android core platform tests, relative to android build root
|
|
# TODO move these test data files to another directory
|
|
_CORE_TEST_PATH = os.path.join("development", "testrunner",
|
|
_TEST_FILE_NAME)
|
|
|
|
# vendor glob file path patterns to tests, relative to android
|
|
# build root
|
|
_VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
|
|
_TEST_FILE_NAME)
|
|
|
|
_RUNTEST_USAGE = (
|
|
"usage: runtest.py [options] short-test-name[s]\n\n"
|
|
"The runtest script works in two ways. You can query it "
|
|
"for a list of tests, or you can launch one or more tests.")
|
|
|
|
def __init__(self):
|
|
# disable logging of timestamp
|
|
self._root_path = android_build.GetTop()
|
|
logger.SetTimestampLogging(False)
|
|
|
|
def _ProcessOptions(self):
|
|
"""Processes command-line options."""
|
|
# TODO error messages on once-only or mutually-exclusive options.
|
|
user_test_default = os.path.join(os.environ.get("HOME"), ".android",
|
|
self._TEST_FILE_NAME)
|
|
|
|
parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
|
|
|
|
parser.add_option("-l", "--list-tests", dest="only_list_tests",
|
|
default=False, action="store_true",
|
|
help="To view the list of tests")
|
|
parser.add_option("-b", "--skip-build", dest="skip_build", default=False,
|
|
action="store_true", help="Skip build - just launch")
|
|
parser.add_option("-n", "--skip_execute", dest="preview", default=False,
|
|
action="store_true",
|
|
help="Do not execute, just preview commands")
|
|
parser.add_option("-r", "--raw-mode", dest="raw_mode", default=False,
|
|
action="store_true",
|
|
help="Raw mode (for output to other tools)")
|
|
parser.add_option("-a", "--suite-assign", dest="suite_assign_mode",
|
|
default=False, action="store_true",
|
|
help="Suite assignment (for details & usage see "
|
|
"InstrumentationTestRunner)")
|
|
parser.add_option("-v", "--verbose", dest="verbose", default=False,
|
|
action="store_true",
|
|
help="Increase verbosity of %s" % sys.argv[0])
|
|
parser.add_option("-w", "--wait-for-debugger", dest="wait_for_debugger",
|
|
default=False, action="store_true",
|
|
help="Wait for debugger before launching tests")
|
|
parser.add_option("-c", "--test-class", dest="test_class",
|
|
help="Restrict test to a specific class")
|
|
parser.add_option("-m", "--test-method", dest="test_method",
|
|
help="Restrict test to a specific method")
|
|
parser.add_option("-p", "--test-package", dest="test_package",
|
|
help="Restrict test to a specific java package")
|
|
parser.add_option("-z", "--size", dest="test_size",
|
|
help="Restrict test to a specific test size")
|
|
parser.add_option("-u", "--user-tests-file", dest="user_tests_file",
|
|
metavar="FILE", default=user_test_default,
|
|
help="Alternate source of user test definitions")
|
|
parser.add_option("-o", "--coverage", dest="coverage",
|
|
default=False, action="store_true",
|
|
help="Generate code coverage metrics for test(s)")
|
|
parser.add_option("-t", "--all-tests", dest="all_tests",
|
|
default=False, action="store_true",
|
|
help="Run all defined tests")
|
|
parser.add_option("--continuous", dest="continuous_tests",
|
|
default=False, action="store_true",
|
|
help="Run all tests defined as part of the continuous "
|
|
"test set")
|
|
parser.add_option("--timeout", dest="timeout",
|
|
default=300, help="Set a timeout limit (in sec) for "
|
|
"running native tests on a device (default: 300 secs)")
|
|
parser.add_option("--cts", dest="cts_tests",
|
|
default=False, action="store_true",
|
|
help="Run all tests defined as part of the "
|
|
"compatibility test suite")
|
|
group = optparse.OptionGroup(
|
|
parser, "Targets", "Use these options to direct tests to a specific "
|
|
"Android target")
|
|
group.add_option("-e", "--emulator", dest="emulator", default=False,
|
|
action="store_true", help="use emulator")
|
|
group.add_option("-d", "--device", dest="device", default=False,
|
|
action="store_true", help="use device")
|
|
group.add_option("-s", "--serial", dest="serial",
|
|
help="use specific serial")
|
|
parser.add_option_group(group)
|
|
|
|
self._options, self._test_args = parser.parse_args()
|
|
|
|
if (not self._options.only_list_tests
|
|
and not self._options.all_tests
|
|
and not self._options.continuous_tests
|
|
and not self._options.cts_tests
|
|
and len(self._test_args) < 1):
|
|
parser.print_help()
|
|
logger.SilentLog("at least one test name must be specified")
|
|
raise errors.AbortError
|
|
|
|
self._adb = adb_interface.AdbInterface()
|
|
if self._options.emulator:
|
|
self._adb.SetEmulatorTarget()
|
|
elif self._options.device:
|
|
self._adb.SetDeviceTarget()
|
|
elif self._options.serial is not None:
|
|
self._adb.SetTargetSerial(self._options.serial)
|
|
|
|
if self._options.verbose:
|
|
logger.SetVerbose(True)
|
|
|
|
self._known_tests = self._ReadTests()
|
|
|
|
self._options.host_lib_path = android_build.GetHostLibraryPath()
|
|
self._options.test_data_path = android_build.GetTestAppPath()
|
|
|
|
def _ReadTests(self):
|
|
"""Parses the set of test definition data.
|
|
|
|
Returns:
|
|
A TestDefinitions object that contains the set of parsed tests.
|
|
Raises:
|
|
AbortError: If a fatal error occurred when parsing the tests.
|
|
"""
|
|
core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
|
|
try:
|
|
known_tests = test_defs.TestDefinitions()
|
|
known_tests.Parse(core_test_path)
|
|
# read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
|
|
vendor_tests_pattern = os.path.join(self._root_path,
|
|
self._VENDOR_TEST_PATH)
|
|
test_file_paths = glob.glob(vendor_tests_pattern)
|
|
for test_file_path in test_file_paths:
|
|
known_tests.Parse(test_file_path)
|
|
if os.path.isfile(self._options.user_tests_file):
|
|
known_tests.Parse(self._options.user_tests_file)
|
|
return known_tests
|
|
except errors.ParseError:
|
|
raise errors.AbortError
|
|
|
|
def _DumpTests(self):
|
|
"""Prints out set of defined tests."""
|
|
print "The following tests are currently defined:"
|
|
for test in self._known_tests:
|
|
print "%-15s %s" % (test.GetName(), test.GetDescription())
|
|
|
|
def _DoBuild(self):
|
|
logger.SilentLog("Building tests...")
|
|
target_set = Set()
|
|
extra_args_set = Set()
|
|
for test_suite in self._GetTestsToRun():
|
|
self._AddBuildTarget(test_suite, target_set, extra_args_set)
|
|
|
|
if target_set:
|
|
if self._options.coverage:
|
|
coverage.EnableCoverageBuild()
|
|
target_build_string = " ".join(list(target_set))
|
|
extra_args_string = " ".join(list(extra_args_set))
|
|
# mmm cannot be used from python, so perform a similar operation using
|
|
# ONE_SHOT_MAKEFILE
|
|
cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files %s' % (
|
|
target_build_string, self._root_path, extra_args_string)
|
|
logger.Log(cmd)
|
|
|
|
if self._options.preview:
|
|
# in preview mode, just display to the user what command would have been
|
|
# run
|
|
logger.Log("adb sync")
|
|
else:
|
|
run_command.RunCommand(cmd, return_output=False)
|
|
logger.Log("Syncing to device...")
|
|
self._adb.Sync()
|
|
|
|
def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
|
|
build_dir = test_suite.GetBuildPath()
|
|
if self._AddBuildTargetPath(build_dir, target_set):
|
|
extra_args_set.add(test_suite.GetExtraBuildArgs())
|
|
for path in test_suite.GetBuildDependencies(self._options):
|
|
self._AddBuildTargetPath(path, target_set)
|
|
|
|
def _AddBuildTargetPath(self, build_dir, target_set):
|
|
if build_dir is not None:
|
|
build_file_path = os.path.join(build_dir, "Android.mk")
|
|
if os.path.isfile(os.path.join(self._root_path, build_file_path)):
|
|
target_set.add(build_file_path)
|
|
return True
|
|
return False
|
|
|
|
def _GetTestsToRun(self):
|
|
"""Get a list of TestSuite objects to run, based on command line args."""
|
|
if self._options.all_tests:
|
|
return self._known_tests.GetTests()
|
|
elif self._options.continuous_tests:
|
|
return self._known_tests.GetContinuousTests()
|
|
elif self._options.cts_tests:
|
|
return self._known_tests.GetCtsTests()
|
|
tests = []
|
|
for name in self._test_args:
|
|
test = self._known_tests.GetTest(name)
|
|
if test is None:
|
|
logger.Log("Error: Could not find test %s" % name)
|
|
self._DumpTests()
|
|
raise errors.AbortError
|
|
tests.append(test)
|
|
return tests
|
|
|
|
def RunTests(self):
|
|
"""Main entry method - executes the tests according to command line args."""
|
|
try:
|
|
run_command.SetAbortOnError()
|
|
self._ProcessOptions()
|
|
if self._options.only_list_tests:
|
|
self._DumpTests()
|
|
return
|
|
|
|
if not self._options.skip_build:
|
|
self._DoBuild()
|
|
|
|
for test_suite in self._GetTestsToRun():
|
|
test_suite.Run(self._options, self._adb)
|
|
|
|
except KeyboardInterrupt:
|
|
logger.Log("Exiting...")
|
|
except errors.AbortError, e:
|
|
logger.Log(e.msg)
|
|
logger.SilentLog("Exiting due to AbortError...")
|
|
except errors.WaitForResponseTimedOutError:
|
|
logger.Log("Timed out waiting for response")
|
|
|
|
|
|
def RunTests():
|
|
runner = TestRunner()
|
|
runner.RunTests()
|
|
|
|
if __name__ == "__main__":
|
|
RunTests()
|