diff options
-rwxr-xr-x | testrunner/adb_interface.py | 6 | ||||
-rw-r--r-- | testrunner/android_mk.py | 93 | ||||
-rw-r--r-- | testrunner/test_defs/gtest.py | 119 | ||||
-rw-r--r-- | testrunner/test_defs/instrumentation_test.py | 144 | ||||
-rw-r--r-- | testrunner/test_defs/test_suite.py | 38 | ||||
-rwxr-xr-x | testrunner/test_defs/test_walker.py | 243 | ||||
-rw-r--r-- | testrunner/tests/Android_gtestlib.mk | 56 | ||||
-rw-r--r-- | testrunner/tests/Android_java.mk | 26 | ||||
-rw-r--r-- | testrunner/tests/Android_native.mk | 21 | ||||
-rwxr-xr-x | testrunner/tests/android_mk_tests.py | 90 |
10 files changed, 676 insertions, 160 deletions
diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py index feb1c893b..ddc50d5b8 100755 --- a/testrunner/adb_interface.py +++ b/testrunner/adb_interface.py @@ -263,8 +263,10 @@ class AdbInterface: inst_command_string = self._BuildInstrumentationCommand( package_name, runner_name, no_window_animation=no_window_animation, raw_mode=raw_mode, instrumentation_args=instrumentation_args) - command_string = "adb %s shell %s" % (self._target_arg, inst_command_string) - return command_string + return self.PreviewShellCommand(inst_command_string) + + def PreviewShellCommand(self, cmd): + return "adb %s shell %s" % (self._target_arg, cmd) def _BuildInstrumentationCommand( self, package, runner_name, no_window_animation=False, profile=False, diff --git a/testrunner/android_mk.py b/testrunner/android_mk.py index 663aa5c14..2c265eea2 100644 --- a/testrunner/android_mk.py +++ b/testrunner/android_mk.py @@ -21,25 +21,26 @@ Specifications for Android.mk can be found at development/ndk/docs/ANDROID-MK.txt """ +import os import re from sets import Set +import logger class AndroidMK(object): """In memory representation of Android.mk file.""" _RE_INCLUDE = re.compile(r'include\s+\$\((.+)\)') + _RE_VARIABLE_REF = re.compile(r'\$\((.+)\)') _VAR_DELIMITER = ":=" FILENAME = "Android.mk" CERTIFICATE = "LOCAL_CERTIFICATE" PACKAGE_NAME = "LOCAL_PACKAGE_NAME" - def __init__(self, app_path=None): + def __init__(self): self._includes = Set() # variables included in makefile self._variables = {} # variables defined in makefile - - if app_path: - self.ParseMK(app_path) + self._has_gtestlib = False def _ProcessMKLine(self, line): """Add a variable definition or include. @@ -56,6 +57,9 @@ class AndroidMK(object): parts = line.split(self._VAR_DELIMITER) if len(parts) > 1: self._variables[parts[0].strip()] = parts[1].strip() + # hack, look for explicit mention of libgtest_main + if line.find('libgtest_main') != -1: + self._has_gtestlib = True def GetVariable(self, identifier): """Retrieve makefile variable. @@ -69,6 +73,38 @@ class AndroidMK(object): # so None is returned if identifier not found return self._variables.get(identifier, None) + def GetExpandedVariable(self, identifier): + """Retrieve makefile variable. + + If variable value refers to another variable, recursively expand it to + find its literal value + + Args: + identifier: name of variable to retrieve + Returns: + value of specified identifier, None if identifier not found in makefile + """ + # use dict.get(x) rather than dict[x] to avoid KeyError exception, + # so None is returned if identifier not found + return self.__RecursiveGetVariable(identifier, Set()) + + def __RecursiveGetVariable(self, identifier, visited_variables): + variable_value = self.GetVariable(identifier) + if not variable_value: + return None + if variable_value in visited_variables: + raise RuntimeError('recursive loop found for makefile variable %s' + % variable_value) + m = self._RE_VARIABLE_REF.match(variable_value) + if m: + logger.SilentLog('Found variable ref %s for identifier %s' + % (variable_value, identifier)) + variable_ref = m.group(1) + visited_variables.add(variable_ref) + return self.__RecursiveGetVariable(variable_ref, visited_variables) + else: + return variable_value + def HasInclude(self, identifier): """Check variable is included in makefile. @@ -79,18 +115,57 @@ class AndroidMK(object): """ return identifier in self._includes - def ParseMK(self, app_path): + def HasJavaLibrary(self, library_name): + """Check if library is specified as a local java library in makefile. + + Args: + library_name: name of library to check + Returns: + True if library_name is included in makefile, otherwise False + """ + java_lib_string = self.GetExpandedVariable('LOCAL_JAVA_LIBRARIES') + if java_lib_string: + java_libs = java_lib_string.split(' ') + return library_name in java_libs + return False + + def HasGTest(self): + """Check if makefile includes rule to build a native gtest. + + Returns: + True if rule to build native test is in makefile, otherwise False + """ + return self._has_gtestlib or self.HasInclude('BUILD_NATIVE_TEST') + + def _ParseMK(self, mk_path): """Parse Android.mk at the specified path. Args: - app_path: path to folder containing Android.mk + mk_path: path to Android.mk Raises: IOError: Android.mk cannot be found at given path, or cannot be opened for reading """ - self.app_path = app_path.rstrip("/") - self.mk_path = "%s/%s" % (self.app_path, self.FILENAME) - mk = open(self.mk_path) + mk = open(mk_path) for line in mk: self._ProcessMKLine(line) mk.close() + + +def CreateAndroidMK(path, filename=AndroidMK.FILENAME): + """Factory method for creating a AndroidMK. + + Args: + path: the directory of the make file + filename: the filename of the makefile + + Return: + the AndroidMK or None if there was no file present + """ + mk_path = os.path.join(path, filename) + if os.path.isfile(mk_path): + mk = AndroidMK() + mk._ParseMK(mk_path) + return mk + else: + return None
\ No newline at end of file diff --git a/testrunner/test_defs/gtest.py b/testrunner/test_defs/gtest.py new file mode 100644 index 000000000..7de674b61 --- /dev/null +++ b/testrunner/test_defs/gtest.py @@ -0,0 +1,119 @@ +#!/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, upstream_build_path=None): + test_suite.AbstractTestFactory.__init__(self, test_root_path, + upstream_build_path=upstream_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] + + """ + 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()) + suite.SetTargetExecPath(os.path.join(target_root_path, 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] + + 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_", name): + logger.SilentLog("Found native test file %s" % file) + test_list.append(name) diff --git a/testrunner/test_defs/instrumentation_test.py b/testrunner/test_defs/instrumentation_test.py index 40d6bedfc..64182a41a 100644 --- a/testrunner/test_defs/instrumentation_test.py +++ b/testrunner/test_defs/instrumentation_test.py @@ -17,7 +17,11 @@ """TestSuite definition for Android instrumentation tests.""" +import os +import re + # local imports +import android_manifest import coverage import errors import logger @@ -193,3 +197,143 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite): total_count+=1 logger.Log("Tests run: %d, Failures: %d, Errors: %d" % (total_count, fail_count, error_count)) + + +class InstrumentationTestFactory(test_suite.AbstractTestFactory): + """A factory for creating InstrumentationTestSuites""" + + def __init__(self, test_root_path, upstream_build_path=None): + test_suite.AbstractTestFactory.__init__(self, test_root_path, + upstream_build_path=upstream_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 + + for instr_name in manifest_parser.GetInstrumentationNames(): + 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) + # this is a bit of a hack, assume if 'com.android.cts' is in + # package name, this is a cts test + # this logic can be removed altogether when cts tests no longer require + # custom build steps + if suite.GetPackageName().startswith('com.android.cts'): + suite.SetSuite('cts') + 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 diff --git a/testrunner/test_defs/test_suite.py b/testrunner/test_defs/test_suite.py index 102a738e2..20f96297c 100644 --- a/testrunner/test_defs/test_suite.py +++ b/testrunner/test_defs/test_suite.py @@ -105,3 +105,41 @@ class AbstractTestSuite(object): adb: asdb_interface to device under test """ raise NotImplementedError + +class AbstractTestFactory(object): + """generic test suite factory.""" + + def __init__(self, test_root_path, upstream_build_path=None): + """Creates a test suite factory. + + Args: + test_root_path: the filesystem path to the tests build directory + upstream_build_path: optional filesystem path for the directory + to build when running tests. If unspecified, will use test_root_path + """ + self._test_root_path = test_root_path + if upstream_build_path: + self._build_path = upstream_build_path + else: + self._build_path = self._test_root_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 diff --git a/testrunner/test_defs/test_walker.py b/testrunner/test_defs/test_walker.py index e34dafc24..de93c808e 100755 --- a/testrunner/test_defs/test_walker.py +++ b/testrunner/test_defs/test_walker.py @@ -19,27 +19,31 @@ # python imports import os -import re # local imports import android_build -import android_manifest import android_mk +import gtest import instrumentation_test import logger class TestWalker(object): - """Finds instrumentation tests from filesystem.""" + """Finds Android tests from filesystem.""" def FindTests(self, path): - """Gets list of Android instrumentation tests found at given path. + """Gets list of Android tests found at given path. - Tests are created from the <instrumentation> tags found in - AndroidManifest.xml files relative to the 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 AndroidManifest.xml is 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: @@ -55,6 +59,8 @@ class TestWalker(object): 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 @@ -110,204 +116,143 @@ class TestWalker(object): # return string with common build_path removed return path[build_path_len:] - def _FindSubTests(self, path, tests, build_path=None): + 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 - build_path: the parent directory where Android.mk that builds sub-folders - was found + 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 - filenames = os.listdir(path) - if filenames.count(android_manifest.AndroidManifest.FILENAME): - # found a manifest! now parse it to find the test definition(s) - manifest = android_manifest.AndroidManifest(app_path=path) - if not build_path: + android_mk_parser = android_mk.CreateAndroidMK(path) + if android_mk_parser: + if not upstream_build_path: # haven't found a parent makefile which builds this dir. Use current # dir as build path - tests.extend(self._CreateSuitesFromManifest( - manifest, self._MakePathRelativeToBuild(path))) + tests.extend(self._CreateSuites( + android_mk_parser, path, self._MakePathRelativeToBuild(path))) else: - tests.extend(self._CreateSuitesFromManifest(manifest, build_path)) - # 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 filenames.count(android_mk.AndroidMK.FILENAME): - android_mk_parser = android_mk.AndroidMK(app_path=path) + tests.extend(self._CreateSuites(android_mk_parser, path, + upstream_build_path)) + # 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.HasInclude('call all-makefiles-under,$(LOCAL_PATH)'): - # found rule to build sub-directories. The parent path can be used, + # found rule to build sub-directories. The parent path can be used, # or if not set, use current path - if not build_path: - build_path = self._MakePathRelativeToBuild(path) + if not upstream_build_path: + upstream_build_path = self._MakePathRelativeToBuild(path) else: - build_path = None - for filename in filenames: - self._FindSubTests(os.path.join(path, filename), tests, build_path) + 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. If it points to a java class file - or java package dir, the appropriate test suite filters will be set + path: the location to start searching. Returns: list of test_suite.AbstractTestSuite found, may be empty """ - class_name_arg = None - package_name = None - # if path is java file, populate class name - if self._IsJavaFile(path): - class_name_arg = self._GetClassNameFromFile(path) - logger.SilentLog('Using java test class %s' % class_name_arg) - elif self._IsJavaPackage(path): - package_name = self._GetPackageNameFromDir(path) - logger.SilentLog('Using java package %s' % package_name) - manifest = self._FindUpstreamManifest(path) - if manifest: - logger.SilentLog('Found AndroidManifest at %s' % manifest.GetAppPath()) - build_path = self._MakePathRelativeToBuild(manifest.GetAppPath()) - return self._CreateSuitesFromManifest(manifest, - build_path, - class_name=class_name_arg, - java_package_name=package_name) - - 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 + factory = self._FindUpstreamTestFactory(path) + if factory: + return factory.CreateTests(sub_tests_path=path) + else: + return [] - 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 _GetTestFactory(self, android_mk_parser, path, upstream_build_path=None): + """Get the test factory for given makefile. - def _GetClassNameFromFile(self, java_file_path): - """Gets the fully qualified java class name from path. + If given path is a valid tests build path, will return the TestFactory + for creating tests. Args: - java_file_path: file system path of java file + android_mk_parser: the android mk to evaluate + path: the filesystem path of the makefile + upstream_build_path: optional filesystem path for the directory + to build when running tests. If unspecified, will use path Returns: - fully qualified java class name or None. + the TestFactory or None if path is not a valid tests build path """ - 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 + if android_mk_parser.HasGTest(): + return gtest.GTestFactory(path, upstream_build_path=upstream_build_path) + elif android_mk_parser.HasJavaLibrary('android.test.runner'): + return instrumentation_test.InstrumentationTestFactory(path, + upstream_build_path=upstream_build_path) + else: + # somewhat unusual, but will continue searching + logger.SilentLog('Found makefile at %s, but did not detect any tests.' + % path) - 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 + return None - 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 _GetTestFactoryForPath(self, path): + """Get the test factory for given path. - def _GetPackageNameFromFile(self, java_file_path): - """Gets the java package name associated with given java file path. + If given path is a valid tests build path, will return the TestFactory + for creating tests. Args: - java_file_path: file system path of java file + path: the filesystem path to evaluate Returns: - the java package name or None + the TestFactory or None if path is not a valid tests build path """ - 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 + android_mk_parser = android_mk.CreateAndroidMK(path) + if android_mk_parser: + return self._GetTestFactory(android_mk_parser, path) + else: + return None - def _FindUpstreamManifest(self, path): - """Recursively searches filesystem upwards for a AndroidManifest file. + def _FindUpstreamTestFactory(self, path): + """Recursively searches filesystem upwards for a test factory. Args: path: file system path to search Returns: - the AndroidManifest found or None + the TestFactory found or None """ - if (os.path.isdir(path) and - os.listdir(path).count(android_manifest.AndroidManifest.FILENAME)): - return android_manifest.AndroidManifest(app_path=path) + factory = self._GetTestFactoryForPath(path) + if factory: + return factory dirpath = os.path.dirname(path) if self._IsPathInBuildTree(path): - return self._FindUpstreamManifest(dirpath) - logger.Log('AndroidManifest.xml not found') + return self._FindUpstreamTestFactory(dirpath) + logger.Log('A tests Android.mk was not found') return None - def _CreateSuitesFromManifest(self, manifest, build_path, class_name=None, - java_package_name=None): - """Creates TestSuites from a AndroidManifest. + def _CreateSuites(self, android_mk_parser, path, upstream_build_path): + """Creates TestSuites from a AndroidMK. Args: - manifest: the AndroidManifest - build_path: the build path to use for test - class_name: optionally, the class filter for the suite - java_package_name: optionally, the java package filter for the suite + 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 """ - tests = [] - for instr_name in manifest.GetInstrumentationNames(): - pkg_name = manifest.GetPackageName() - if instr_name.find(".") < 0: - instr_name = "." + instr_name - logger.SilentLog('Found instrumentation %s/%s' % (pkg_name, instr_name)) - suite = instrumentation_test.InstrumentationTestSuite() - suite.SetPackageName(pkg_name) - suite.SetBuildPath(build_path) - suite.SetRunnerName(instr_name) - suite.SetName(pkg_name) - suite.SetClassName(class_name) - suite.SetJavaPackageFilter(java_package_name) - # this is a bit of a hack, assume if 'com.android.cts' is in - # package name, this is a cts test - # this logic can be removed altogether when cts tests no longer require - # custom build steps - if suite.GetPackageName().startswith('com.android.cts'): - suite.SetSuite('cts') - tests.append(suite) - return tests + factory = self._GetTestFactory(android_mk_parser, path, + upstream_build_path=upstream_build_path) + if factory: + return factory.CreateTests(path) + else: + return [] diff --git a/testrunner/tests/Android_gtestlib.mk b/testrunner/tests/Android_gtestlib.mk new file mode 100644 index 000000000..0d3ec8faf --- /dev/null +++ b/testrunner/tests/Android_gtestlib.mk @@ -0,0 +1,56 @@ +# Copyright (C) 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. + +# Build the unit tests. +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +# Build the unit tests. +test_src_files := $(call find-subdir-files, *.cpp) + +shared_libraries := \ + libz \ + liblog \ + libcutils \ + libutils \ + libstlport + +static_libraries := \ + libgtest \ + libgtest_main + +c_includes := \ + external/zlib \ + external/icu4c/common \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport + +module_tags := eng tests + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) + +endif diff --git a/testrunner/tests/Android_java.mk b/testrunner/tests/Android_java.mk new file mode 100644 index 000000000..ff9138a53 --- /dev/null +++ b/testrunner/tests/Android_java.mk @@ -0,0 +1,26 @@ +# Copyright (C) 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests +LOCAL_JAVA_LIBRARIES := foo android.test.runner +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_PACKAGE_NAME := ApiDemosTests +LOCAL_INSTRUMENTATION_FOR := ApiDemos +LOCAL_SDK_VERSION := current + +include $(BUILD_PACKAGE) diff --git a/testrunner/tests/Android_native.mk b/testrunner/tests/Android_native.mk new file mode 100644 index 000000000..6273ccbd8 --- /dev/null +++ b/testrunner/tests/Android_native.mk @@ -0,0 +1,21 @@ +# Copyright (C) 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. + +include $(CLEAR_VARS) +test_module := foo +LOCAL_MODULE := $(test_module) +recursive_var := $(recursive_var) +LOCAL_MODULE_TAGS := tags +LOCAL_SRC_FILES := src +include $(BUILD_NATIVE_TEST) diff --git a/testrunner/tests/android_mk_tests.py b/testrunner/tests/android_mk_tests.py new file mode 100755 index 000000000..ff1f2896c --- /dev/null +++ b/testrunner/tests/android_mk_tests.py @@ -0,0 +1,90 @@ +#!/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. + +import sys +import unittest +sys.path.append('../..') + +from testrunner import android_mk + + +class AndroidMKTest(unittest.TestCase): + """Unit tests for AndroidMK.""" + + def testHasGTest(self): + """Test for AndroidMK.HasGTest.""" + mk_parser = android_mk.CreateAndroidMK(path='.', + filename='Android_native.mk') + self.assertTrue(mk_parser.HasGTest()) + + def testHasGTest_lib(self): + """Test for AndroidMK.HasGTest.""" + mk_parser = android_mk.CreateAndroidMK(path='.', + filename='Android_gtestlib.mk') + self.assertTrue(mk_parser.HasGTest()) + + def testHasGTest_false(self): + """Negative test for AndroidMK.HasGTest.""" + mk_parser = android_mk.CreateAndroidMK(path='.', filename='Android_java.mk') + self.assertFalse(mk_parser.HasGTest()) + + def testHasJavaLibrary(self): + """Test for AndroidMK.HasJavaLibrary.""" + mk_parser = android_mk.CreateAndroidMK(path='.', + filename='Android_java.mk') + self.assertTrue(mk_parser.HasJavaLibrary('android.test.runner')) + + def testHasJavaLibrary_missing(self): + """Negative test for AndroidMK.HasJavaLibrary. + + Test behavior when LOCAL_JAVA_LIBARIES rule is not present in makefile. + """ + mk_parser = android_mk.CreateAndroidMK(path='.', + filename='Android_native.mk') + self.assertFalse(mk_parser.HasJavaLibrary('android.test.runner')) + + def testHasJavaLibrary_false(self): + """Negative test for AndroidMK.HasJavaLibrary. + + Test behavior when LOCAL_JAVA_LIBARIES rule is present, but does not list + given library. + """ + mk_parser = android_mk.CreateAndroidMK(path='.', filename='Android_java.mk') + self.assertFalse(mk_parser.HasJavaLibrary('doesntexist')) + + def testGetExpandedVariable(self): + """Test for AndroidMK.GetExpandedVariable. + """ + mk_parser = android_mk.CreateAndroidMK(path='.', + filename='Android_native.mk') + self.assertEquals('foo', mk_parser.GetExpandedVariable('LOCAL_MODULE')) + + def testGetExpandedVariable_loop(self): + """Test for AndroidMK.GetExpandedVariable where variable expansion loops + """ + mk_parser = android_mk.CreateAndroidMK(path='.', + filename='Android_native.mk') + try: + mk_parser.GetExpandedVariable('recursive_var') + self.assertTrue(False) + except RuntimeError: + # expected + pass + + +if __name__ == '__main__': + unittest.main() |