summaryrefslogtreecommitdiffstats
path: root/testrunner/test_defs
diff options
context:
space:
mode:
Diffstat (limited to 'testrunner/test_defs')
-rw-r--r--testrunner/test_defs/gtest.py119
-rw-r--r--testrunner/test_defs/instrumentation_test.py144
-rw-r--r--testrunner/test_defs/test_suite.py38
-rwxr-xr-xtestrunner/test_defs/test_walker.py243
4 files changed, 395 insertions, 149 deletions
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 []