upload android base code part7
This commit is contained in:
parent
4e516ec6ed
commit
841ae54672
25229 changed files with 1709508 additions and 0 deletions
1
android/development/testrunner/test_defs/__init__.py
Normal file
1
android/development/testrunner/test_defs/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
__all__ = ['test_defs', 'test_walker']
|
122
android/development/testrunner/test_defs/gtest.py
Normal file
122
android/development/testrunner/test_defs/gtest.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
#
|
||||
# Copyright 2011, 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.
|
||||
|
||||
"""TestSuite for running C/C++ Android tests using gtest framework."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
import re
|
||||
|
||||
# local imports
|
||||
import logger
|
||||
import run_command
|
||||
import test_suite
|
||||
|
||||
|
||||
class GTestSuite(test_suite.AbstractTestSuite):
|
||||
"""A test suite for running gtest on device."""
|
||||
|
||||
def __init__(self):
|
||||
test_suite.AbstractTestSuite.__init__(self)
|
||||
self._target_exec_path = None
|
||||
|
||||
def GetTargetExecPath(self):
|
||||
"""Get the target path to gtest executable."""
|
||||
return self._target_exec_path
|
||||
|
||||
def SetTargetExecPath(self, path):
|
||||
self._target_exec_path = path
|
||||
return self
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Run the provided gtest test suite.
|
||||
|
||||
Args:
|
||||
options: command line options
|
||||
adb: adb interface
|
||||
"""
|
||||
shell_cmd = adb.PreviewShellCommand(self.GetTargetExecPath())
|
||||
logger.Log(shell_cmd)
|
||||
if not options.preview:
|
||||
# gtest will log to test results to stdout, so no need to do any
|
||||
# extra processing
|
||||
run_command.RunCommand(shell_cmd, return_output=False)
|
||||
|
||||
|
||||
class GTestFactory(test_suite.AbstractTestFactory):
|
||||
|
||||
def __init__(self, test_root_path, build_path):
|
||||
test_suite.AbstractTestFactory.__init__(self, test_root_path,
|
||||
build_path)
|
||||
|
||||
def CreateTests(self, sub_tests_path=None):
|
||||
"""Create tests found in sub_tests_path.
|
||||
|
||||
Looks for test files matching a pattern, and assumes each one is a separate
|
||||
binary on target.
|
||||
|
||||
Test files must match one of the following pattern:
|
||||
- test_*.[c|cc|cpp]
|
||||
- *_test.[c|cc|cpp]
|
||||
- *_unittest.[c|cc|cpp]
|
||||
- *Tests.[cc|cpp]
|
||||
|
||||
"""
|
||||
if not sub_tests_path:
|
||||
sub_tests_path = self.GetTestRootPath()
|
||||
test_file_list = []
|
||||
if os.path.isfile(sub_tests_path):
|
||||
self._EvaluateFile(test_file_list, os.path.basename(sub_tests_path))
|
||||
else:
|
||||
os.path.walk(sub_tests_path, self._CollectTestSources, test_file_list)
|
||||
# TODO: obtain this from makefile instead of hardcoding
|
||||
target_root_path = os.path.join('/data', 'nativetest')
|
||||
test_suites = []
|
||||
for test_file in test_file_list:
|
||||
logger.SilentLog('Creating gtest suite for file %s' % test_file)
|
||||
suite = GTestSuite()
|
||||
suite.SetBuildPath(self.GetBuildPath())
|
||||
# expect tests in /data/nativetest/test_file/test_file
|
||||
suite.SetTargetExecPath(os.path.join(target_root_path, test_file, test_file))
|
||||
test_suites.append(suite)
|
||||
return test_suites
|
||||
|
||||
def _CollectTestSources(self, test_list, dirname, files):
|
||||
"""For each directory, find tests source file and add them to the list.
|
||||
|
||||
Test files must match one of the following pattern:
|
||||
- test_*.[cc|cpp]
|
||||
- *_test.[cc|cpp]
|
||||
- *_unittest.[cc|cpp]
|
||||
- *Tests.[cc|cpp]
|
||||
|
||||
This method is a callback for os.path.walk.
|
||||
|
||||
Args:
|
||||
test_list: Where new tests should be inserted.
|
||||
dirname: Current directory.
|
||||
files: List of files in the current directory.
|
||||
"""
|
||||
for f in files:
|
||||
self._EvaluateFile(test_list, f)
|
||||
|
||||
def _EvaluateFile(self, test_list, file):
|
||||
(name, ext) = os.path.splitext(file)
|
||||
if ext == ".cc" or ext == ".cpp" or ext == ".c":
|
||||
if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_|Tests$", name):
|
||||
logger.SilentLog("Found native test file %s" % file)
|
||||
test_list.append(name)
|
108
android/development/testrunner/test_defs/host_test.py
Normal file
108
android/development/testrunner/test_defs/host_test.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""Parser for test definition xml files."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
|
||||
import errors
|
||||
import logger
|
||||
import run_command
|
||||
import test_suite
|
||||
|
||||
|
||||
class HostTestSuite(test_suite.AbstractTestSuite):
|
||||
"""A test suite for running hosttestlib java tests."""
|
||||
|
||||
_JUNIT_JAR_NAME = "junit.jar"
|
||||
_HOSTTESTLIB_NAME = "hosttestlib.jar"
|
||||
_DDMLIB_NAME = "ddmlib-prebuilt.jar"
|
||||
_TRADEFED_NAME = "tradefed-prebuilt.jar"
|
||||
_lib_names = [_JUNIT_JAR_NAME, _HOSTTESTLIB_NAME, _DDMLIB_NAME, _TRADEFED_NAME]
|
||||
|
||||
_JUNIT_BUILD_PATH = os.path.join("external", "junit")
|
||||
_HOSTTESTLIB_BUILD_PATH = os.path.join("development", "tools", "hosttestlib")
|
||||
_LIB_BUILD_PATHS = [_JUNIT_BUILD_PATH, _HOSTTESTLIB_BUILD_PATH ]
|
||||
|
||||
# main class for running host tests
|
||||
# TODO: should other runners be supported, and make runner an attribute of
|
||||
# the test suite?
|
||||
_TEST_RUNNER = "com.android.hosttest.DeviceTestRunner"
|
||||
|
||||
def __init__(self):
|
||||
test_suite.AbstractTestSuite.__init__(self)
|
||||
self._jar_name = None
|
||||
self._class_name = None
|
||||
|
||||
def GetBuildDependencies(self, options):
|
||||
"""Override parent to tag on building host libs."""
|
||||
return self._LIB_BUILD_PATHS
|
||||
|
||||
def GetClassName(self):
|
||||
return self._class_name
|
||||
|
||||
def SetClassName(self, class_name):
|
||||
self._class_name = class_name
|
||||
return self
|
||||
|
||||
def GetJarName(self):
|
||||
"""Returns the name of the host jar that contains the tests."""
|
||||
return self._jar_name
|
||||
|
||||
def SetJarName(self, jar_name):
|
||||
self._jar_name = jar_name
|
||||
return self
|
||||
|
||||
def Run(self, options, adb_interface):
|
||||
"""Runs the host test.
|
||||
|
||||
Results will be displayed on stdout. Assumes 'java' is on system path.
|
||||
|
||||
Args:
|
||||
options: command line options for running host tests. Expected member
|
||||
fields:
|
||||
host_lib_path: path to directory that contains host library files
|
||||
test_data_path: path to directory that contains test data files
|
||||
preview: if true, do not execute, display commands only
|
||||
adb_interface: reference to device under test
|
||||
|
||||
Raises:
|
||||
errors.AbortError: if fatal error occurs
|
||||
"""
|
||||
# get the serial number of the device under test, so it can be passed to
|
||||
# hosttestlib.
|
||||
serial_number = adb_interface.GetSerialNumber()
|
||||
self._lib_names.append(self.GetJarName())
|
||||
# gather all the host jars that are needed to run tests
|
||||
full_lib_paths = []
|
||||
for lib in self._lib_names:
|
||||
path = os.path.join(options.host_lib_path, lib)
|
||||
# make sure jar file exists on host
|
||||
if not os.path.exists(path):
|
||||
raise errors.AbortError(msg="Could not find jar %s" % path)
|
||||
full_lib_paths.append(path)
|
||||
|
||||
# java -cp <libs> <runner class> <test suite class> -s <device serial>
|
||||
# -p <test data path>
|
||||
cmd = "java -cp %s %s %s -s %s -p %s" % (":".join(full_lib_paths),
|
||||
self._TEST_RUNNER,
|
||||
self.GetClassName(), serial_number,
|
||||
options.test_data_path)
|
||||
logger.Log(cmd)
|
||||
if not options.preview:
|
||||
run_command.RunOnce(cmd, return_output=False)
|
360
android/development/testrunner/test_defs/instrumentation_test.py
Normal file
360
android/development/testrunner/test_defs/instrumentation_test.py
Normal file
|
@ -0,0 +1,360 @@
|
|||
#!/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.
|
||||
|
||||
"""TestSuite definition for Android instrumentation tests."""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
# local imports
|
||||
import android_manifest
|
||||
from coverage import coverage
|
||||
import errors
|
||||
import logger
|
||||
import test_suite
|
||||
|
||||
|
||||
class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
||||
"""Represents a java instrumentation test suite definition run on device."""
|
||||
|
||||
DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
|
||||
|
||||
def __init__(self):
|
||||
test_suite.AbstractTestSuite.__init__(self)
|
||||
self._package_name = None
|
||||
self._runner_name = self.DEFAULT_RUNNER
|
||||
self._class_name = None
|
||||
self._target_name = None
|
||||
self._java_package = None
|
||||
|
||||
def GetPackageName(self):
|
||||
return self._package_name
|
||||
|
||||
def SetPackageName(self, package_name):
|
||||
self._package_name = package_name
|
||||
return self
|
||||
|
||||
def GetRunnerName(self):
|
||||
return self._runner_name
|
||||
|
||||
def SetRunnerName(self, runner_name):
|
||||
self._runner_name = runner_name
|
||||
return self
|
||||
|
||||
def GetClassName(self):
|
||||
return self._class_name
|
||||
|
||||
def SetClassName(self, class_name):
|
||||
self._class_name = class_name
|
||||
return self
|
||||
|
||||
def GetJavaPackageFilter(self):
|
||||
return self._java_package
|
||||
|
||||
def SetJavaPackageFilter(self, java_package_name):
|
||||
"""Configure the suite to only run tests in given java package."""
|
||||
self._java_package = java_package_name
|
||||
return self
|
||||
|
||||
def GetTargetName(self):
|
||||
"""Retrieve module that this test is targeting.
|
||||
|
||||
Used for generating code coverage metrics.
|
||||
Returns:
|
||||
the module target name
|
||||
"""
|
||||
return self._target_name
|
||||
|
||||
def SetTargetName(self, target_name):
|
||||
self._target_name = target_name
|
||||
return self
|
||||
|
||||
def GetBuildDependencies(self, options):
|
||||
if options.coverage_target_path:
|
||||
return [options.coverage_target_path]
|
||||
return []
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Run the provided test suite.
|
||||
|
||||
Builds up an adb instrument command using provided input arguments.
|
||||
|
||||
Args:
|
||||
options: command line options to provide to test run
|
||||
adb: adb_interface to device under test
|
||||
|
||||
Raises:
|
||||
errors.AbortError: if fatal error occurs
|
||||
"""
|
||||
|
||||
test_class = self.GetClassName()
|
||||
if options.test_class is not None:
|
||||
test_class = options.test_class.lstrip()
|
||||
if test_class.startswith("."):
|
||||
test_class = self.GetPackageName() + test_class
|
||||
if options.test_method is not None:
|
||||
test_class = "%s#%s" % (test_class, options.test_method)
|
||||
|
||||
test_package = self.GetJavaPackageFilter()
|
||||
if options.test_package:
|
||||
test_package = options.test_package
|
||||
|
||||
if test_class and test_package:
|
||||
logger.Log('Error: both class and java package options are specified')
|
||||
|
||||
instrumentation_args = {}
|
||||
if test_class is not None:
|
||||
instrumentation_args["class"] = test_class
|
||||
if test_package:
|
||||
instrumentation_args["package"] = test_package
|
||||
if options.test_size:
|
||||
instrumentation_args["size"] = options.test_size
|
||||
if options.wait_for_debugger:
|
||||
instrumentation_args["debug"] = "true"
|
||||
if options.suite_assign_mode:
|
||||
instrumentation_args["suiteAssignment"] = "true"
|
||||
if options.coverage:
|
||||
instrumentation_args["coverage"] = "true"
|
||||
if options.test_annotation:
|
||||
instrumentation_args["annotation"] = options.test_annotation
|
||||
if options.test_not_annotation:
|
||||
instrumentation_args["notAnnotation"] = options.test_not_annotation
|
||||
if options.preview:
|
||||
adb_cmd = adb.PreviewInstrumentationCommand(
|
||||
package_name=self.GetPackageName(),
|
||||
runner_name=self.GetRunnerName(),
|
||||
raw_mode=options.raw_mode,
|
||||
instrumentation_args=instrumentation_args)
|
||||
logger.Log(adb_cmd)
|
||||
elif options.coverage:
|
||||
coverage_gen = coverage.CoverageGenerator(adb)
|
||||
if options.coverage_target_path:
|
||||
coverage_target = coverage_gen.GetCoverageTargetForPath(options.coverage_target_path)
|
||||
elif self.GetTargetName():
|
||||
coverage_target = coverage_gen.GetCoverageTarget(self.GetTargetName())
|
||||
self._CheckInstrumentationInstalled(adb)
|
||||
# need to parse test output to determine path to coverage file
|
||||
logger.Log("Running in coverage mode, suppressing test output")
|
||||
try:
|
||||
(test_results, status_map) = adb.StartInstrumentationForPackage(
|
||||
package_name=self.GetPackageName(),
|
||||
runner_name=self.GetRunnerName(),
|
||||
timeout_time=60*60,
|
||||
instrumentation_args=instrumentation_args,
|
||||
user=options.user)
|
||||
except errors.InstrumentationError, errors.DeviceUnresponsiveError:
|
||||
return
|
||||
self._PrintTestResults(test_results)
|
||||
device_coverage_path = status_map.get("coverageFilePath", None)
|
||||
if device_coverage_path is None:
|
||||
logger.Log("Error: could not find coverage data on device")
|
||||
return
|
||||
|
||||
coverage_file = coverage_gen.ExtractReport(
|
||||
self.GetName(), coverage_target, device_coverage_path,
|
||||
test_qualifier=options.test_size)
|
||||
if coverage_file is not None:
|
||||
logger.Log("Coverage report generated at %s" % coverage_file)
|
||||
|
||||
else:
|
||||
self._CheckInstrumentationInstalled(adb)
|
||||
adb.StartInstrumentationNoResults(package_name=self.GetPackageName(),
|
||||
runner_name=self.GetRunnerName(),
|
||||
raw_mode=options.raw_mode,
|
||||
instrumentation_args=
|
||||
instrumentation_args,
|
||||
user=options.user)
|
||||
|
||||
def _CheckInstrumentationInstalled(self, adb):
|
||||
if not adb.IsInstrumentationInstalled(self.GetPackageName(),
|
||||
self.GetRunnerName()):
|
||||
msg=("Could not find instrumentation %s/%s on device. Try forcing a "
|
||||
"rebuild by updating a source file, and re-executing runtest." %
|
||||
(self.GetPackageName(), self.GetRunnerName()))
|
||||
raise errors.AbortError(msg=msg)
|
||||
|
||||
def _PrintTestResults(self, test_results):
|
||||
"""Prints a summary of test result data to stdout.
|
||||
|
||||
Args:
|
||||
test_results: a list of am_instrument_parser.TestResult
|
||||
"""
|
||||
total_count = 0
|
||||
error_count = 0
|
||||
fail_count = 0
|
||||
for test_result in test_results:
|
||||
if test_result.GetStatusCode() == -1: # error
|
||||
logger.Log("Error in %s: %s" % (test_result.GetTestName(),
|
||||
test_result.GetFailureReason()))
|
||||
error_count+=1
|
||||
elif test_result.GetStatusCode() == -2: # failure
|
||||
logger.Log("Failure in %s: %s" % (test_result.GetTestName(),
|
||||
test_result.GetFailureReason()))
|
||||
fail_count+=1
|
||||
total_count+=1
|
||||
logger.Log("Tests run: %d, Failures: %d, Errors: %d" %
|
||||
(total_count, fail_count, error_count))
|
||||
|
||||
def HasInstrumentationTest(path):
|
||||
"""Determine if given path defines an instrumentation test.
|
||||
|
||||
Args:
|
||||
path: file system path to instrumentation test.
|
||||
"""
|
||||
manifest_parser = android_manifest.CreateAndroidManifest(path)
|
||||
if manifest_parser:
|
||||
return manifest_parser.GetInstrumentationNames()
|
||||
return False
|
||||
|
||||
class InstrumentationTestFactory(test_suite.AbstractTestFactory):
|
||||
"""A factory for creating InstrumentationTestSuites"""
|
||||
|
||||
def __init__(self, test_root_path, build_path):
|
||||
test_suite.AbstractTestFactory.__init__(self, test_root_path,
|
||||
build_path)
|
||||
|
||||
def CreateTests(self, sub_tests_path=None):
|
||||
"""Create tests found in test_path.
|
||||
|
||||
Will create a single InstrumentationTestSuite based on info found in
|
||||
AndroidManifest.xml found at build_path. Will set additional filters if
|
||||
test_path refers to a java package or java class.
|
||||
"""
|
||||
tests = []
|
||||
class_name_arg = None
|
||||
java_package_name = None
|
||||
if sub_tests_path:
|
||||
# if path is java file, populate class name
|
||||
if self._IsJavaFile(sub_tests_path):
|
||||
class_name_arg = self._GetClassNameFromFile(sub_tests_path)
|
||||
logger.SilentLog('Using java test class %s' % class_name_arg)
|
||||
elif self._IsJavaPackage(sub_tests_path):
|
||||
java_package_name = self._GetPackageNameFromDir(sub_tests_path)
|
||||
logger.SilentLog('Using java package %s' % java_package_name)
|
||||
try:
|
||||
manifest_parser = android_manifest.AndroidManifest(app_path=
|
||||
self.GetTestsRootPath())
|
||||
instrs = manifest_parser.GetInstrumentationNames()
|
||||
if not instrs:
|
||||
logger.Log('Could not find instrumentation declarations in %s at %s' %
|
||||
(android_manifest.AndroidManifest.FILENAME,
|
||||
self.GetBuildPath()))
|
||||
return tests
|
||||
elif len(instrs) > 1:
|
||||
logger.Log("Found multiple instrumentation declarations in %s/%s. "
|
||||
"Only using first declared." %
|
||||
(self.GetBuildPath(),
|
||||
android_manifest.AndroidManifest.FILENAME))
|
||||
instr_name = manifest_parser.GetInstrumentationNames()[0]
|
||||
# escape inner class names
|
||||
instr_name = instr_name.replace('$', '\$')
|
||||
pkg_name = manifest_parser.GetPackageName()
|
||||
if instr_name.find(".") < 0:
|
||||
instr_name = "." + instr_name
|
||||
logger.SilentLog('Found instrumentation %s/%s' % (pkg_name, instr_name))
|
||||
suite = InstrumentationTestSuite()
|
||||
suite.SetPackageName(pkg_name)
|
||||
suite.SetBuildPath(self.GetBuildPath())
|
||||
suite.SetRunnerName(instr_name)
|
||||
suite.SetName(pkg_name)
|
||||
suite.SetClassName(class_name_arg)
|
||||
suite.SetJavaPackageFilter(java_package_name)
|
||||
tests.append(suite)
|
||||
return tests
|
||||
|
||||
except:
|
||||
logger.Log('Could not find or parse %s at %s' %
|
||||
(android_manifest.AndroidManifest.FILENAME,
|
||||
self.GetBuildPath()))
|
||||
return tests
|
||||
|
||||
def _IsJavaFile(self, path):
|
||||
"""Returns true if given file system path is a java file."""
|
||||
return os.path.isfile(path) and self._IsJavaFileName(path)
|
||||
|
||||
def _IsJavaFileName(self, filename):
|
||||
"""Returns true if given file name is a java file name."""
|
||||
return os.path.splitext(filename)[1] == '.java'
|
||||
|
||||
def _IsJavaPackage(self, path):
|
||||
"""Returns true if given file path is a java package.
|
||||
|
||||
Currently assumes if any java file exists in this directory, than it
|
||||
represents a java package.
|
||||
|
||||
Args:
|
||||
path: file system path of directory to check
|
||||
|
||||
Returns:
|
||||
True if path is a java package
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
return False
|
||||
for file_name in os.listdir(path):
|
||||
if self._IsJavaFileName(file_name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _GetClassNameFromFile(self, java_file_path):
|
||||
"""Gets the fully qualified java class name from path.
|
||||
|
||||
Args:
|
||||
java_file_path: file system path of java file
|
||||
|
||||
Returns:
|
||||
fully qualified java class name or None.
|
||||
"""
|
||||
package_name = self._GetPackageNameFromFile(java_file_path)
|
||||
if package_name:
|
||||
filename = os.path.basename(java_file_path)
|
||||
class_name = os.path.splitext(filename)[0]
|
||||
return '%s.%s' % (package_name, class_name)
|
||||
return None
|
||||
|
||||
def _GetPackageNameFromDir(self, path):
|
||||
"""Gets the java package name associated with given directory path.
|
||||
|
||||
Caveat: currently just parses defined java package name from first java
|
||||
file found in directory.
|
||||
|
||||
Args:
|
||||
path: file system path of directory
|
||||
|
||||
Returns:
|
||||
the java package name or None
|
||||
"""
|
||||
for filename in os.listdir(path):
|
||||
if self._IsJavaFileName(filename):
|
||||
return self._GetPackageNameFromFile(os.path.join(path, filename))
|
||||
|
||||
def _GetPackageNameFromFile(self, java_file_path):
|
||||
"""Gets the java package name associated with given java file path.
|
||||
|
||||
Args:
|
||||
java_file_path: file system path of java file
|
||||
|
||||
Returns:
|
||||
the java package name or None
|
||||
"""
|
||||
logger.SilentLog('Looking for java package name in %s' % java_file_path)
|
||||
re_package = re.compile(r'package\s+(.*);')
|
||||
file_handle = open(java_file_path, 'r')
|
||||
for line in file_handle:
|
||||
match = re_package.match(line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
185
android/development/testrunner/test_defs/native_test.py
Normal file
185
android/development/testrunner/test_defs/native_test.py
Normal file
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""TestSuite for running native Android tests."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
import re
|
||||
|
||||
# local imports
|
||||
import android_build
|
||||
import logger
|
||||
import run_command
|
||||
import test_suite
|
||||
|
||||
|
||||
class NativeTestSuite(test_suite.AbstractTestSuite):
|
||||
"""A test suite for running native aka C/C++ tests on device."""
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Run the provided *native* test suite.
|
||||
|
||||
The test_suite must contain a build path where the native test
|
||||
files are. Subdirectories are automatically scanned as well.
|
||||
|
||||
Each test's name must have a .cc or .cpp extension and match one
|
||||
of the following patterns:
|
||||
- test_*
|
||||
- *_test.[cc|cpp]
|
||||
- *_unittest.[cc|cpp]
|
||||
A successful test must return 0. Any other value will be considered
|
||||
as an error.
|
||||
|
||||
Args:
|
||||
options: command line options
|
||||
adb: adb interface
|
||||
"""
|
||||
# find all test files, convert unicode names to ascii, take the basename
|
||||
# and drop the .cc/.cpp extension.
|
||||
source_list = []
|
||||
build_path = os.path.join(android_build.GetTop(), self.GetBuildPath())
|
||||
os.path.walk(build_path, self._CollectTestSources, source_list)
|
||||
logger.SilentLog("Tests source %s" % source_list)
|
||||
|
||||
# Host tests are under out/host/<os>-<arch>/bin.
|
||||
host_list = self._FilterOutMissing(android_build.GetHostBin(), source_list)
|
||||
logger.SilentLog("Host tests %s" % host_list)
|
||||
|
||||
# Target tests are under $ANDROID_PRODUCT_OUT/data/nativetest.
|
||||
target_list = self._FilterOutMissing(android_build.GetTargetNativeTestPath(),
|
||||
source_list)
|
||||
logger.SilentLog("Target tests %s" % target_list)
|
||||
|
||||
# Run on the host
|
||||
logger.Log("\nRunning on host")
|
||||
for f in host_list:
|
||||
if run_command.RunHostCommand(f) != 0:
|
||||
logger.Log("%s... failed" % f)
|
||||
else:
|
||||
if run_command.HasValgrind():
|
||||
if run_command.RunHostCommand(f, valgrind=True) == 0:
|
||||
logger.Log("%s... ok\t\t[valgrind: ok]" % f)
|
||||
else:
|
||||
logger.Log("%s... ok\t\t[valgrind: failed]" % f)
|
||||
else:
|
||||
logger.Log("%s... ok\t\t[valgrind: missing]" % f)
|
||||
|
||||
# Run on the device
|
||||
logger.Log("\nRunning on target")
|
||||
for f in target_list:
|
||||
full_path = os.path.join(os.sep, "data", "nativetest", f)
|
||||
|
||||
# Single quotes are needed to prevent the shell splitting it.
|
||||
output = adb.SendShellCommand("'%s 2>&1;echo -n exit code:$?'" %
|
||||
"(cd /sdcard;%s)" % full_path,
|
||||
int(options.timeout))
|
||||
success = output.endswith("exit code:0")
|
||||
logger.Log("%s... %s" % (f, success and "ok" or "failed"))
|
||||
# Print the captured output when the test failed.
|
||||
if not success or options.verbose:
|
||||
pos = output.rfind("exit code")
|
||||
output = output[0:pos]
|
||||
logger.Log(output)
|
||||
|
||||
# Cleanup
|
||||
adb.SendShellCommand("rm %s" % full_path)
|
||||
|
||||
def _CollectTestSources(self, test_list, dirname, files):
|
||||
"""For each directory, find tests source file and add them to the list.
|
||||
|
||||
Test files must match one of the following pattern:
|
||||
- test_*.[cc|cpp]
|
||||
- *_test.[cc|cpp]
|
||||
- *_unittest.[cc|cpp]
|
||||
|
||||
This method is a callback for os.path.walk.
|
||||
|
||||
Args:
|
||||
test_list: Where new tests should be inserted.
|
||||
dirname: Current directory.
|
||||
files: List of files in the current directory.
|
||||
"""
|
||||
for f in files:
|
||||
(name, ext) = os.path.splitext(f)
|
||||
if ext == ".cc" or ext == ".cpp" or ext == ".c":
|
||||
if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
|
||||
logger.SilentLog("Found %s" % f)
|
||||
test_list.append(str(os.path.join(dirname, f)))
|
||||
|
||||
def _FilterOutMissing(self, path, sources):
|
||||
"""Filter out from the sources list missing tests.
|
||||
|
||||
Sometimes some test source are not built for the target, i.e there
|
||||
is no binary corresponding to the source file. We need to filter
|
||||
these out.
|
||||
|
||||
Args:
|
||||
path: Where the binaries should be.
|
||||
sources: List of tests source path.
|
||||
Returns:
|
||||
A list of relative paths to the test binaries built from the sources.
|
||||
"""
|
||||
binaries = []
|
||||
for f in sources:
|
||||
binary = os.path.basename(f)
|
||||
binary = os.path.splitext(binary)[0]
|
||||
found = self._FindFileRecursively(path, binary)
|
||||
if found:
|
||||
binary = os.path.relpath(os.path.abspath(found),
|
||||
os.path.abspath(path))
|
||||
binaries.append(binary)
|
||||
return binaries
|
||||
|
||||
def _FindFileRecursively(self, path, match):
|
||||
"""Finds the first executable binary in a given path that matches the name.
|
||||
|
||||
Args:
|
||||
path: Where to search for binaries. Can be nested directories.
|
||||
binary: Which binary to search for.
|
||||
Returns:
|
||||
first matched file in the path or None if none is found.
|
||||
"""
|
||||
for root, dirs, files in os.walk(path):
|
||||
for f in files:
|
||||
if f == match:
|
||||
return os.path.join(root, f)
|
||||
for d in dirs:
|
||||
found = self._FindFileRecursively(os.path.join(root, d), match)
|
||||
if found:
|
||||
return found
|
||||
return None
|
||||
|
||||
def _RunHostCommand(self, binary, valgrind=False):
|
||||
"""Run a command on the host (opt using valgrind).
|
||||
|
||||
Runs the host binary and returns the exit code.
|
||||
If successfull, the output (stdout and stderr) are discarded,
|
||||
but printed in case of error.
|
||||
The command can be run under valgrind in which case all the
|
||||
output are always discarded.
|
||||
|
||||
Args:
|
||||
binary: basename of the file to be run. It is expected to be under
|
||||
$ANDROID_HOST_OUT/bin.
|
||||
valgrind: If True the command will be run under valgrind.
|
||||
|
||||
Returns:
|
||||
The command exit code (int)
|
||||
"""
|
||||
full_path = os.path.join(android_build.GetHostBin(), binary)
|
||||
return run_command.RunHostCommand(full_path, valgrind=valgrind)
|
132
android/development/testrunner/test_defs/test_defs.py
Normal file
132
android/development/testrunner/test_defs/test_defs.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
#!/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.
|
||||
|
||||
"""Parser for test definition xml files."""
|
||||
|
||||
# Python imports
|
||||
import xml.dom.minidom
|
||||
import xml.parsers
|
||||
|
||||
# local imports
|
||||
import errors
|
||||
import logger
|
||||
import xml_suite_helper
|
||||
|
||||
|
||||
class TestDefinitions(object):
|
||||
"""Accessor for a test definitions xml file data.
|
||||
|
||||
See test_defs.xsd for expected format.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# dictionary of test name to tests
|
||||
self._testname_map = {}
|
||||
|
||||
def __iter__(self):
|
||||
ordered_list = []
|
||||
for k in sorted(self._testname_map):
|
||||
ordered_list.append(self._testname_map[k])
|
||||
return iter(ordered_list)
|
||||
|
||||
def Parse(self, file_path):
|
||||
"""Parse the test suite data from from given file path.
|
||||
|
||||
Args:
|
||||
file_path: absolute file path to parse
|
||||
Raises:
|
||||
ParseError if file_path cannot be parsed
|
||||
"""
|
||||
try:
|
||||
doc = xml.dom.minidom.parse(file_path)
|
||||
self._ParseDoc(doc)
|
||||
except IOError:
|
||||
logger.Log("test file %s does not exist" % file_path)
|
||||
raise errors.ParseError
|
||||
except xml.parsers.expat.ExpatError:
|
||||
logger.Log("Error Parsing xml file: %s " % file_path)
|
||||
raise errors.ParseError
|
||||
except errors.ParseError, e:
|
||||
logger.Log("Error Parsing xml file: %s Reason: %s" % (file_path, e.msg))
|
||||
raise e
|
||||
|
||||
def ParseString(self, xml_string):
|
||||
"""Alternate parse method that accepts a string of the xml data."""
|
||||
doc = xml.dom.minidom.parseString(xml_string)
|
||||
# TODO: catch exceptions and raise ParseError
|
||||
return self._ParseDoc(doc)
|
||||
|
||||
def _ParseDoc(self, doc):
|
||||
root_element = self._GetRootElement(doc)
|
||||
suite_parser = xml_suite_helper.XmlSuiteParser()
|
||||
for element in root_element.childNodes:
|
||||
if element.nodeType != xml.dom.Node.ELEMENT_NODE:
|
||||
continue
|
||||
test_suite = suite_parser.Parse(element)
|
||||
if test_suite:
|
||||
self._AddTest(test_suite)
|
||||
|
||||
def _GetRootElement(self, doc):
|
||||
root_elements = doc.getElementsByTagName("test-definitions")
|
||||
if len(root_elements) != 1:
|
||||
error_msg = "expected 1 and only one test-definitions tag"
|
||||
raise errors.ParseError(msg=error_msg)
|
||||
return root_elements[0]
|
||||
|
||||
def _AddTest(self, test):
|
||||
"""Adds a test to this TestManifest.
|
||||
|
||||
If a test already exists with the same name, it overrides it.
|
||||
|
||||
Args:
|
||||
test: TestSuite to add
|
||||
"""
|
||||
if self.GetTest(test.GetName()) is not None:
|
||||
logger.SilentLog("Overriding test definition %s" % test.GetName())
|
||||
self._testname_map[test.GetName()] = test
|
||||
|
||||
def GetTests(self):
|
||||
return self._testname_map.values()
|
||||
|
||||
def GetContinuousTests(self):
|
||||
con_tests = []
|
||||
for test in self.GetTests():
|
||||
if test.IsContinuous():
|
||||
con_tests.append(test)
|
||||
return con_tests
|
||||
|
||||
def GetTestsInSuite(self, suite):
|
||||
"""Return list of tests in given suite."""
|
||||
return [t for t in self.GetTests() if t.GetSuite() == suite]
|
||||
|
||||
def GetTest(self, name):
|
||||
return self._testname_map.get(name, None)
|
||||
|
||||
|
||||
def Parse(file_path):
|
||||
"""Parses out a TestDefinitions from given path to xml file.
|
||||
|
||||
Args:
|
||||
file_path: string absolute file path
|
||||
Returns:
|
||||
a TestDefinitions object containing data parsed from file_path
|
||||
Raises:
|
||||
ParseError if xml format is not recognized
|
||||
"""
|
||||
tests_result = TestDefinitions()
|
||||
tests_result.Parse(file_path)
|
||||
return tests_result
|
151
android/development/testrunner/test_defs/test_suite.py
Normal file
151
android/development/testrunner/test_defs/test_suite.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""Abstract Android test suite."""
|
||||
|
||||
|
||||
class AbstractTestSuite(object):
|
||||
"""Represents a generic test suite definition.
|
||||
|
||||
TODO: rename this as AbstractTestDef.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._name = None
|
||||
self._build_path = None
|
||||
self._build_dependencies = []
|
||||
self._is_continuous = False
|
||||
self._suite = None
|
||||
self._description = ''
|
||||
self._extra_build_args = ''
|
||||
self._is_full_make = False
|
||||
self._is_granted_permissions = True
|
||||
|
||||
def GetName(self):
|
||||
return self._name
|
||||
|
||||
def SetName(self, name):
|
||||
self._name = name
|
||||
return self
|
||||
|
||||
def GetBuildPath(self):
|
||||
"""Returns the build path of this test, relative to source tree root."""
|
||||
return self._build_path
|
||||
|
||||
def SetBuildPath(self, build_path):
|
||||
self._build_path = build_path
|
||||
return self
|
||||
|
||||
def GetBuildDependencies(self, options):
|
||||
"""Returns a list of dependent build paths."""
|
||||
return self._build_dependencies
|
||||
|
||||
def SetBuildDependencies(self, build_dependencies):
|
||||
self._build_dependencies = build_dependencies
|
||||
return self
|
||||
|
||||
def IsContinuous(self):
|
||||
"""Returns true if test is part of the continuous test."""
|
||||
return self._is_continuous
|
||||
|
||||
def SetContinuous(self, continuous):
|
||||
self._is_continuous = continuous
|
||||
return self._is_continuous
|
||||
|
||||
def IsGrantedPermissions(self):
|
||||
"""Return true if the test should be granted runtime permissions on install."""
|
||||
return self._is_granted_permissions
|
||||
|
||||
def SetIsGrantedPermissions(self, is_granted_permissions):
|
||||
self._is_granted_permissions = is_granted_permissions
|
||||
return self._is_granted_permissions
|
||||
|
||||
def GetSuite(self):
|
||||
"""Returns the name of test' suite, or None."""
|
||||
return self._suite
|
||||
|
||||
def SetSuite(self, suite):
|
||||
self._suite = suite
|
||||
return self
|
||||
|
||||
def GetDescription(self):
|
||||
"""Returns a description if available, an empty string otherwise."""
|
||||
return self._description
|
||||
|
||||
def SetDescription(self, desc):
|
||||
self._description = desc
|
||||
return self
|
||||
|
||||
def GetExtraBuildArgs(self):
|
||||
"""Returns the extra build args if available, an empty string otherwise."""
|
||||
return self._extra_build_args
|
||||
|
||||
def SetExtraBuildArgs(self, build_args):
|
||||
self._extra_build_args = build_args
|
||||
return self
|
||||
|
||||
def IsFullMake(self):
|
||||
return self._is_full_make
|
||||
|
||||
def SetIsFullMake(self, full_make):
|
||||
self._is_full_make = full_make
|
||||
return self
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Runs the test.
|
||||
|
||||
Subclasses must implement this.
|
||||
Args:
|
||||
options: global command line options
|
||||
adb: asdb_interface to device under test
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
class AbstractTestFactory(object):
|
||||
"""generic test suite factory."""
|
||||
|
||||
def __init__(self, test_root_path, build_path):
|
||||
"""Creates a test suite factory.
|
||||
|
||||
Args:
|
||||
test_root_path: the filesystem path to the tests build directory
|
||||
upstream_build_path: filesystem path for the directory
|
||||
to build when running tests, relative to the source tree root.
|
||||
"""
|
||||
self._test_root_path = test_root_path
|
||||
self._build_path = build_path
|
||||
|
||||
def GetBuildPath(self):
|
||||
return self._build_path
|
||||
|
||||
def GetTestsRootPath(self):
|
||||
return self._test_root_path
|
||||
|
||||
def CreateTests(self, sub_tests_path=None):
|
||||
"""Creates the tests at given test_path.
|
||||
|
||||
Subclasses must implement this.
|
||||
|
||||
Args:
|
||||
sub_tests_path: the child path of test_root_path containing the tests to
|
||||
run. If unspecified will be set to test_root_path.
|
||||
|
||||
Returns:
|
||||
an array of AbstractTestSuite, or empty AbstractTestSuite if no tests
|
||||
were defined
|
||||
"""
|
||||
raise NotImplementedError
|
263
android/development/testrunner/test_defs/test_walker.py
Executable file
263
android/development/testrunner/test_defs/test_walker.py
Executable file
|
@ -0,0 +1,263 @@
|
|||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""Utility to find instrumentation test definitions from file system."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
|
||||
# local imports
|
||||
import android_build
|
||||
import android_mk
|
||||
import gtest
|
||||
import instrumentation_test
|
||||
import logger
|
||||
|
||||
|
||||
class TestWalker(object):
|
||||
"""Finds Android tests from filesystem."""
|
||||
|
||||
def FindTests(self, path):
|
||||
"""Gets list of Android tests found at given path.
|
||||
|
||||
Tests are created from info found in Android.mk and AndroidManifest.xml
|
||||
files relative to the given path.
|
||||
|
||||
Currently supported tests are:
|
||||
- Android application tests run via instrumentation
|
||||
- native C/C++ tests using GTest framework. (note Android.mk must follow
|
||||
expected GTest template)
|
||||
|
||||
FindTests will first scan sub-folders of path for tests. If none are found,
|
||||
it will scan the file system upwards until a valid test Android.mk is found
|
||||
or the Android build root is reached.
|
||||
|
||||
Some sample values for path:
|
||||
- a parent directory containing many tests:
|
||||
ie development/samples will return tests for instrumentation's in ApiDemos,
|
||||
ApiDemos/tests, Notepad/tests etc
|
||||
- a java test class file
|
||||
ie ApiDemos/tests/src/../ApiDemosTest.java will return a test for
|
||||
the instrumentation in ApiDemos/tests, with the class name filter set to
|
||||
ApiDemosTest
|
||||
- a java package directory
|
||||
ie ApiDemos/tests/src/com/example/android/apis will return a test for
|
||||
the instrumentation in ApiDemos/tests, with the java package filter set
|
||||
to com.example.android.apis.
|
||||
|
||||
TODO: add GTest examples
|
||||
|
||||
Args:
|
||||
path: file system path to search
|
||||
|
||||
Returns:
|
||||
list of test suites that support operations defined by
|
||||
test_suite.AbstractTestSuite
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
logger.Log('%s does not exist' % path)
|
||||
return []
|
||||
realpath = os.path.realpath(path)
|
||||
# ensure path is in ANDROID_BUILD_ROOT
|
||||
self._build_top = os.path.realpath(android_build.GetTop())
|
||||
if not self._IsPathInBuildTree(realpath):
|
||||
logger.Log('%s is not a sub-directory of build root %s' %
|
||||
(path, self._build_top))
|
||||
return []
|
||||
|
||||
# first, assume path is a parent directory, which specifies to run all
|
||||
# tests within this directory
|
||||
tests = self._FindSubTests(realpath, [])
|
||||
if not tests:
|
||||
logger.SilentLog('No tests found within %s, searching upwards' % path)
|
||||
tests = self._FindUpstreamTests(realpath)
|
||||
return tests
|
||||
|
||||
def _IsPathInBuildTree(self, path):
|
||||
"""Return true if given path is within current Android build tree.
|
||||
|
||||
Args:
|
||||
path: absolute file system path
|
||||
|
||||
Returns:
|
||||
True if path is within Android build tree
|
||||
"""
|
||||
return os.path.commonprefix([self._build_top, path]) == self._build_top
|
||||
|
||||
def _MakePathRelativeToBuild(self, path):
|
||||
"""Convert given path to one relative to build tree root.
|
||||
|
||||
Args:
|
||||
path: absolute file system path to convert.
|
||||
|
||||
Returns:
|
||||
The converted path relative to build tree root.
|
||||
|
||||
Raises:
|
||||
ValueError: if path is not within build tree
|
||||
"""
|
||||
if not self._IsPathInBuildTree(path):
|
||||
raise ValueError
|
||||
build_path_len = len(self._build_top) + 1
|
||||
# return string with common build_path removed
|
||||
return path[build_path_len:]
|
||||
|
||||
def _FindSubTests(self, path, tests, upstream_build_path=None):
|
||||
"""Recursively finds all tests within given path.
|
||||
|
||||
Args:
|
||||
path: absolute file system path to check
|
||||
tests: current list of found tests
|
||||
upstream_build_path: the parent directory where Android.mk that builds
|
||||
sub-folders was found
|
||||
|
||||
Returns:
|
||||
updated list of tests
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
return tests
|
||||
android_mk_parser = android_mk.CreateAndroidMK(path)
|
||||
if android_mk_parser:
|
||||
build_rel_path = self._MakePathRelativeToBuild(path)
|
||||
if not upstream_build_path:
|
||||
# haven't found a parent makefile which builds this dir. Use current
|
||||
# dir as build path
|
||||
tests.extend(self._CreateSuites(
|
||||
android_mk_parser, path, build_rel_path))
|
||||
else:
|
||||
tests.extend(self._CreateSuites(android_mk_parser, path,
|
||||
upstream_build_path))
|
||||
# TODO: remove this logic, and rely on caller to collapse build
|
||||
# paths via make_tree
|
||||
|
||||
# Try to build as much of original path as possible, so
|
||||
# keep track of upper-most parent directory where Android.mk was found
|
||||
# that has rule to build sub-directory makefiles.
|
||||
# this is also necessary in case of overlapping tests
|
||||
# ie if a test exists at 'foo' directory and 'foo/sub', attempting to
|
||||
# build both 'foo' and 'foo/sub' will fail.
|
||||
|
||||
if android_mk_parser.IncludesMakefilesUnder():
|
||||
# found rule to build sub-directories. The parent path can be used,
|
||||
# or if not set, use current path
|
||||
if not upstream_build_path:
|
||||
upstream_build_path = self._MakePathRelativeToBuild(path)
|
||||
else:
|
||||
upstream_build_path = None
|
||||
for filename in os.listdir(path):
|
||||
self._FindSubTests(os.path.join(path, filename), tests,
|
||||
upstream_build_path)
|
||||
return tests
|
||||
|
||||
def _FindUpstreamTests(self, path):
|
||||
"""Find tests defined upward from given path.
|
||||
|
||||
Args:
|
||||
path: the location to start searching.
|
||||
|
||||
Returns:
|
||||
list of test_suite.AbstractTestSuite found, may be empty
|
||||
"""
|
||||
factory = self._FindUpstreamTestFactory(path)
|
||||
if factory:
|
||||
return factory.CreateTests(sub_tests_path=path)
|
||||
else:
|
||||
return []
|
||||
|
||||
def _GetTestFactory(self, android_mk_parser, path, build_path):
|
||||
"""Get the test factory for given makefile.
|
||||
|
||||
If given path is a valid tests build path, will return the TestFactory
|
||||
for creating tests.
|
||||
|
||||
Args:
|
||||
android_mk_parser: the android mk to evaluate
|
||||
path: the filesystem path of the makefile
|
||||
build_path: filesystem path for the directory
|
||||
to build when running tests, relative to source root.
|
||||
|
||||
Returns:
|
||||
the TestFactory or None if path is not a valid tests build path
|
||||
"""
|
||||
if android_mk_parser.HasGTest():
|
||||
return gtest.GTestFactory(path, build_path)
|
||||
elif instrumentation_test.HasInstrumentationTest(path):
|
||||
return instrumentation_test.InstrumentationTestFactory(path,
|
||||
build_path)
|
||||
else:
|
||||
# somewhat unusual, but will continue searching
|
||||
logger.SilentLog('Found makefile at %s, but did not detect any tests.'
|
||||
% path)
|
||||
|
||||
return None
|
||||
|
||||
def _GetTestFactoryForPath(self, path):
|
||||
"""Get the test factory for given path.
|
||||
|
||||
If given path is a valid tests build path, will return the TestFactory
|
||||
for creating tests.
|
||||
|
||||
Args:
|
||||
path: the filesystem path to evaluate
|
||||
|
||||
Returns:
|
||||
the TestFactory or None if path is not a valid tests build path
|
||||
"""
|
||||
android_mk_parser = android_mk.CreateAndroidMK(path)
|
||||
if android_mk_parser:
|
||||
build_path = self._MakePathRelativeToBuild(path)
|
||||
return self._GetTestFactory(android_mk_parser, path, build_path)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _FindUpstreamTestFactory(self, path):
|
||||
"""Recursively searches filesystem upwards for a test factory.
|
||||
|
||||
Args:
|
||||
path: file system path to search
|
||||
|
||||
Returns:
|
||||
the TestFactory found or None
|
||||
"""
|
||||
factory = self._GetTestFactoryForPath(path)
|
||||
if factory:
|
||||
return factory
|
||||
dirpath = os.path.dirname(path)
|
||||
if self._IsPathInBuildTree(path):
|
||||
return self._FindUpstreamTestFactory(dirpath)
|
||||
logger.Log('A tests Android.mk was not found')
|
||||
return None
|
||||
|
||||
def _CreateSuites(self, android_mk_parser, path, upstream_build_path):
|
||||
"""Creates TestSuites from a AndroidMK.
|
||||
|
||||
Args:
|
||||
android_mk_parser: the AndroidMK
|
||||
path: absolute file system path of the makefile to evaluate
|
||||
upstream_build_path: the build path to use for test. This can be
|
||||
different than the 'path', in cases where an upstream makefile
|
||||
is being used.
|
||||
|
||||
Returns:
|
||||
the list of tests created
|
||||
"""
|
||||
factory = self._GetTestFactory(android_mk_parser, path,
|
||||
build_path=upstream_build_path)
|
||||
if factory:
|
||||
return factory.CreateTests(path)
|
||||
else:
|
||||
return []
|
162
android/development/testrunner/test_defs/xml_suite_helper.py
Normal file
162
android/development/testrunner/test_defs/xml_suite_helper.py
Normal file
|
@ -0,0 +1,162 @@
|
|||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""Utility to parse suite info from xml."""
|
||||
|
||||
# Python imports
|
||||
import xml.dom.minidom
|
||||
import xml.parsers
|
||||
|
||||
# local imports
|
||||
import errors
|
||||
import logger
|
||||
import host_test
|
||||
import instrumentation_test
|
||||
import native_test
|
||||
|
||||
|
||||
class XmlSuiteParser(object):
|
||||
"""Parses XML attributes common to all TestSuite's."""
|
||||
|
||||
# common attributes
|
||||
_NAME_ATTR = 'name'
|
||||
_BUILD_ATTR = 'build_path'
|
||||
_CONTINUOUS_ATTR = 'continuous'
|
||||
_SUITE_ATTR = 'suite'
|
||||
_DESCRIPTION_ATTR = 'description'
|
||||
_EXTRA_BUILD_ARGS_ATTR = 'extra_build_args'
|
||||
_FULL_MAKE_ATTR = 'full_make'
|
||||
_GRANTED_PERMISSIONS_ATTR = 'granted_permissions'
|
||||
|
||||
def Parse(self, element):
|
||||
"""Populates common suite attributes from given suite xml element.
|
||||
|
||||
Args:
|
||||
element: xml node to parse
|
||||
Raises:
|
||||
ParseError if a required attribute is missing.
|
||||
Returns:
|
||||
parsed test suite or None
|
||||
"""
|
||||
parser = None
|
||||
if element.nodeName == InstrumentationParser.TAG_NAME:
|
||||
parser = InstrumentationParser()
|
||||
elif element.nodeName == NativeParser.TAG_NAME:
|
||||
parser = NativeParser()
|
||||
elif element.nodeName == HostParser.TAG_NAME:
|
||||
parser = HostParser()
|
||||
else:
|
||||
logger.Log('Unrecognized tag %s found' % element.nodeName)
|
||||
return None
|
||||
test_suite = parser.Parse(element)
|
||||
return test_suite
|
||||
|
||||
def _ParseCommonAttributes(self, suite_element, test_suite):
|
||||
test_suite.SetName(self._ParseAttribute(suite_element, self._NAME_ATTR,
|
||||
True))
|
||||
test_suite.SetBuildPath(self._ParseAttribute(suite_element,
|
||||
self._BUILD_ATTR, True))
|
||||
test_suite.SetContinuous(self._ParseAttribute(suite_element,
|
||||
self._CONTINUOUS_ATTR,
|
||||
False, default_value=False))
|
||||
test_suite.SetIsGrantedPermissions(self._ParseAttribute(suite_element,
|
||||
self._GRANTED_PERMISSIONS_ATTR,
|
||||
False, default_value=True))
|
||||
test_suite.SetSuite(self._ParseAttribute(suite_element, self._SUITE_ATTR, False,
|
||||
default_value=None))
|
||||
test_suite.SetDescription(self._ParseAttribute(suite_element,
|
||||
self._DESCRIPTION_ATTR,
|
||||
False,
|
||||
default_value=''))
|
||||
test_suite.SetExtraBuildArgs(self._ParseAttribute(
|
||||
suite_element, self._EXTRA_BUILD_ARGS_ATTR, False, default_value=''))
|
||||
test_suite.SetIsFullMake(self._ParseAttribute(
|
||||
suite_element, self._FULL_MAKE_ATTR, False, default_value=False))
|
||||
|
||||
|
||||
def _ParseAttribute(self, suite_element, attribute_name, mandatory,
|
||||
default_value=None):
|
||||
if suite_element.hasAttribute(attribute_name):
|
||||
value = suite_element.getAttribute(attribute_name)
|
||||
if default_value in (True, False):
|
||||
value = value.lower() == "true"
|
||||
elif mandatory:
|
||||
error_msg = ('Could not find attribute %s in %s' %
|
||||
(attribute_name, self.TAG_NAME))
|
||||
raise errors.ParseError(msg=error_msg)
|
||||
else:
|
||||
value = default_value
|
||||
return value
|
||||
|
||||
|
||||
class InstrumentationParser(XmlSuiteParser):
|
||||
"""Parses instrumentation suite attributes from xml."""
|
||||
|
||||
# for legacy reasons, the xml tag name for java (device) tests is 'test'
|
||||
TAG_NAME = 'test'
|
||||
|
||||
_PKG_ATTR = 'package'
|
||||
_RUNNER_ATTR = 'runner'
|
||||
_CLASS_ATTR = 'class'
|
||||
_TARGET_ATTR = 'coverage_target'
|
||||
|
||||
def Parse(self, suite_element):
|
||||
"""Creates suite and populate with data from xml element."""
|
||||
suite = instrumentation_test.InstrumentationTestSuite()
|
||||
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
|
||||
suite.SetPackageName(self._ParseAttribute(suite_element, self._PKG_ATTR,
|
||||
True))
|
||||
suite.SetRunnerName(self._ParseAttribute(
|
||||
suite_element, self._RUNNER_ATTR, False,
|
||||
instrumentation_test.InstrumentationTestSuite.DEFAULT_RUNNER))
|
||||
suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
|
||||
False))
|
||||
suite.SetTargetName(self._ParseAttribute(suite_element, self._TARGET_ATTR,
|
||||
False))
|
||||
return suite
|
||||
|
||||
|
||||
class NativeParser(XmlSuiteParser):
|
||||
"""Parses native suite attributes from xml."""
|
||||
|
||||
TAG_NAME = 'test-native'
|
||||
|
||||
def Parse(self, suite_element):
|
||||
"""Creates suite and populate with data from xml element."""
|
||||
suite = native_test.NativeTestSuite()
|
||||
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
|
||||
return suite
|
||||
|
||||
|
||||
class HostParser(XmlSuiteParser):
|
||||
"""Parses host suite attributes from xml."""
|
||||
|
||||
TAG_NAME = 'test-host'
|
||||
|
||||
_CLASS_ATTR = 'class'
|
||||
# TODO: consider obsoleting in favor of parsing the Android.mk to find the
|
||||
# jar name
|
||||
_JAR_ATTR = 'jar_name'
|
||||
|
||||
def Parse(self, suite_element):
|
||||
"""Creates suite and populate with data from xml element."""
|
||||
suite = host_test.HostTestSuite()
|
||||
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
|
||||
suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
|
||||
True))
|
||||
suite.SetJarName(self._ParseAttribute(suite_element, self._JAR_ATTR, True))
|
||||
return suite
|
Loading…
Add table
Add a link
Reference in a new issue