| #!/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 [] |