Home | History | Annotate | Download | only in test_defs
      1 #!/usr/bin/python2.4
      2 #
      3 #
      4 # Copyright 2009, The Android Open Source Project
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #     http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 
     18 """Utility to find instrumentation test definitions from file system."""
     19 
     20 # python imports
     21 import os
     22 
     23 # local imports
     24 import android_build
     25 import android_mk
     26 import gtest
     27 import instrumentation_test
     28 import logger
     29 
     30 
     31 class TestWalker(object):
     32   """Finds Android tests from filesystem."""
     33 
     34   def FindTests(self, path):
     35     """Gets list of Android tests found at given path.
     36 
     37     Tests are created from info found in Android.mk and AndroidManifest.xml
     38     files relative to the given path.
     39 
     40     Currently supported tests are:
     41     - Android application tests run via instrumentation
     42     - native C/C++ tests using GTest framework. (note Android.mk must follow
     43       expected GTest template)
     44 
     45     FindTests will first scan sub-folders of path for tests. If none are found,
     46     it will scan the file system upwards until a valid test Android.mk is found
     47     or the Android build root is reached.
     48 
     49     Some sample values for path:
     50     - a parent directory containing many tests:
     51     ie development/samples will return tests for instrumentation's in ApiDemos,
     52     ApiDemos/tests, Notepad/tests etc
     53     - a java test class file
     54     ie ApiDemos/tests/src/../ApiDemosTest.java will return a test for
     55     the instrumentation in ApiDemos/tests, with the class name filter set to
     56     ApiDemosTest
     57     - a java package directory
     58     ie ApiDemos/tests/src/com/example/android/apis will return a test for
     59     the instrumentation in ApiDemos/tests, with the java package filter set
     60     to com.example.android.apis.
     61 
     62     TODO: add GTest examples
     63 
     64     Args:
     65       path: file system path to search
     66 
     67     Returns:
     68       list of test suites that support operations defined by
     69       test_suite.AbstractTestSuite
     70     """
     71     if not os.path.exists(path):
     72       logger.Log('%s does not exist' % path)
     73       return []
     74     realpath = os.path.realpath(path)
     75     # ensure path is in ANDROID_BUILD_ROOT
     76     self._build_top = os.path.realpath(android_build.GetTop())
     77     if not self._IsPathInBuildTree(realpath):
     78       logger.Log('%s is not a sub-directory of build root %s' %
     79                  (path, self._build_top))
     80       return []
     81 
     82     # first, assume path is a parent directory, which specifies to run all
     83     # tests within this directory
     84     tests = self._FindSubTests(realpath, [])
     85     if not tests:
     86       logger.SilentLog('No tests found within %s, searching upwards' % path)
     87       tests = self._FindUpstreamTests(realpath)
     88     return tests
     89 
     90   def _IsPathInBuildTree(self, path):
     91     """Return true if given path is within current Android build tree.
     92 
     93     Args:
     94       path: absolute file system path
     95 
     96     Returns:
     97       True if path is within Android build tree
     98     """
     99     return os.path.commonprefix([self._build_top, path]) == self._build_top
    100 
    101   def _MakePathRelativeToBuild(self, path):
    102     """Convert given path to one relative to build tree root.
    103 
    104     Args:
    105       path: absolute file system path to convert.
    106 
    107     Returns:
    108       The converted path relative to build tree root.
    109 
    110     Raises:
    111       ValueError: if path is not within build tree
    112     """
    113     if not self._IsPathInBuildTree(path):
    114       raise ValueError
    115     build_path_len = len(self._build_top) + 1
    116     # return string with common build_path removed
    117     return path[build_path_len:]
    118 
    119   def _FindSubTests(self, path, tests, upstream_build_path=None):
    120     """Recursively finds all tests within given path.
    121 
    122     Args:
    123       path: absolute file system path to check
    124       tests: current list of found tests
    125       upstream_build_path: the parent directory where Android.mk that builds
    126         sub-folders was found
    127 
    128     Returns:
    129       updated list of tests
    130     """
    131     if not os.path.isdir(path):
    132       return tests
    133     android_mk_parser = android_mk.CreateAndroidMK(path)
    134     if android_mk_parser:
    135       build_rel_path = self._MakePathRelativeToBuild(path)
    136       if not upstream_build_path:
    137         # haven't found a parent makefile which builds this dir. Use current
    138         # dir as build path
    139         tests.extend(self._CreateSuites(
    140             android_mk_parser, path, build_rel_path))
    141       else:
    142         tests.extend(self._CreateSuites(android_mk_parser, path,
    143                                         upstream_build_path))
    144       # Try to build as much of original path as possible, so
    145       # keep track of upper-most parent directory where Android.mk was found
    146       # that has rule to build sub-directory makefiles.
    147       # this is also necessary in case of overlapping tests
    148       # ie if a test exists at 'foo' directory  and 'foo/sub', attempting to
    149       # build both 'foo' and 'foo/sub' will fail.
    150 
    151       if android_mk_parser.HasInclude('call all-makefiles-under,$(LOCAL_PATH)'):
    152         # found rule to build sub-directories. The parent path can be used,
    153         # or if not set, use current path
    154         if not upstream_build_path:
    155           upstream_build_path = self._MakePathRelativeToBuild(path)
    156       else:
    157         upstream_build_path = None
    158     for filename in os.listdir(path):
    159       self._FindSubTests(os.path.join(path, filename), tests,
    160                          upstream_build_path)
    161     return tests
    162 
    163   def _FindUpstreamTests(self, path):
    164     """Find tests defined upward from given path.
    165 
    166     Args:
    167       path: the location to start searching.
    168 
    169     Returns:
    170       list of test_suite.AbstractTestSuite found, may be empty
    171     """
    172     factory = self._FindUpstreamTestFactory(path)
    173     if factory:
    174       return factory.CreateTests(sub_tests_path=path)
    175     else:
    176       return []
    177 
    178   def _GetTestFactory(self, android_mk_parser, path, build_path):
    179     """Get the test factory for given makefile.
    180 
    181     If given path is a valid tests build path, will return the TestFactory
    182     for creating tests.
    183 
    184     Args:
    185       android_mk_parser: the android mk to evaluate
    186       path: the filesystem path of the makefile
    187       build_path: filesystem path for the directory
    188       to build when running tests, relative to source root.
    189 
    190     Returns:
    191       the TestFactory or None if path is not a valid tests build path
    192     """
    193     if android_mk_parser.HasGTest():
    194       return gtest.GTestFactory(path, build_path)
    195     elif instrumentation_test.HasInstrumentationTest(path):
    196       return instrumentation_test.InstrumentationTestFactory(path,
    197           build_path)
    198     else:
    199       # somewhat unusual, but will continue searching
    200       logger.SilentLog('Found makefile at %s, but did not detect any tests.'
    201                        % path)
    202 
    203     return None
    204 
    205   def _GetTestFactoryForPath(self, path):
    206     """Get the test factory for given path.
    207 
    208     If given path is a valid tests build path, will return the TestFactory
    209     for creating tests.
    210 
    211     Args:
    212       path: the filesystem path to evaluate
    213 
    214     Returns:
    215       the TestFactory or None if path is not a valid tests build path
    216     """
    217     android_mk_parser = android_mk.CreateAndroidMK(path)
    218     if android_mk_parser:
    219       build_path = self._MakePathRelativeToBuild(path)
    220       return self._GetTestFactory(android_mk_parser, path, build_path)
    221     else:
    222       return None
    223 
    224   def _FindUpstreamTestFactory(self, path):
    225     """Recursively searches filesystem upwards for a test factory.
    226 
    227     Args:
    228       path: file system path to search
    229 
    230     Returns:
    231       the TestFactory found or None
    232     """
    233     factory = self._GetTestFactoryForPath(path)
    234     if factory:
    235       return factory
    236     dirpath = os.path.dirname(path)
    237     if self._IsPathInBuildTree(path):
    238       return self._FindUpstreamTestFactory(dirpath)
    239     logger.Log('A tests Android.mk was not found')
    240     return None
    241 
    242   def _CreateSuites(self, android_mk_parser, path, upstream_build_path):
    243     """Creates TestSuites from a AndroidMK.
    244 
    245     Args:
    246       android_mk_parser: the AndroidMK
    247       path: absolute file system path of the makefile to evaluate
    248       upstream_build_path: the build path to use for test. This can be
    249         different than the 'path', in cases where an upstream makefile
    250         is being used.
    251 
    252     Returns:
    253       the list of tests created
    254     """
    255     factory = self._GetTestFactory(android_mk_parser, path,
    256                                    build_path=upstream_build_path)
    257     if factory:
    258       return factory.CreateTests(path)
    259     else:
    260       return []
    261