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       # TODO: remove this logic, and rely on caller to collapse build
    145       # paths via make_tree
    146 
    147       # Try to build as much of original path as possible, so
    148       # keep track of upper-most parent directory where Android.mk was found
    149       # that has rule to build sub-directory makefiles.
    150       # this is also necessary in case of overlapping tests
    151       # ie if a test exists at 'foo' directory  and 'foo/sub', attempting to
    152       # build both 'foo' and 'foo/sub' will fail.
    153 
    154       if android_mk_parser.IncludesMakefilesUnder():
    155         # found rule to build sub-directories. The parent path can be used,
    156         # or if not set, use current path
    157         if not upstream_build_path:
    158           upstream_build_path = self._MakePathRelativeToBuild(path)
    159       else:
    160         upstream_build_path = None
    161     for filename in os.listdir(path):
    162       self._FindSubTests(os.path.join(path, filename), tests,
    163                          upstream_build_path)
    164     return tests
    165 
    166   def _FindUpstreamTests(self, path):
    167     """Find tests defined upward from given path.
    168 
    169     Args:
    170       path: the location to start searching.
    171 
    172     Returns:
    173       list of test_suite.AbstractTestSuite found, may be empty
    174     """
    175     factory = self._FindUpstreamTestFactory(path)
    176     if factory:
    177       return factory.CreateTests(sub_tests_path=path)
    178     else:
    179       return []
    180 
    181   def _GetTestFactory(self, android_mk_parser, path, build_path):
    182     """Get the test factory for given makefile.
    183 
    184     If given path is a valid tests build path, will return the TestFactory
    185     for creating tests.
    186 
    187     Args:
    188       android_mk_parser: the android mk to evaluate
    189       path: the filesystem path of the makefile
    190       build_path: filesystem path for the directory
    191       to build when running tests, relative to source root.
    192 
    193     Returns:
    194       the TestFactory or None if path is not a valid tests build path
    195     """
    196     if android_mk_parser.HasGTest():
    197       return gtest.GTestFactory(path, build_path)
    198     elif instrumentation_test.HasInstrumentationTest(path):
    199       return instrumentation_test.InstrumentationTestFactory(path,
    200           build_path)
    201     else:
    202       # somewhat unusual, but will continue searching
    203       logger.SilentLog('Found makefile at %s, but did not detect any tests.'
    204                        % path)
    205 
    206     return None
    207 
    208   def _GetTestFactoryForPath(self, path):
    209     """Get the test factory for given path.
    210 
    211     If given path is a valid tests build path, will return the TestFactory
    212     for creating tests.
    213 
    214     Args:
    215       path: the filesystem path to evaluate
    216 
    217     Returns:
    218       the TestFactory or None if path is not a valid tests build path
    219     """
    220     android_mk_parser = android_mk.CreateAndroidMK(path)
    221     if android_mk_parser:
    222       build_path = self._MakePathRelativeToBuild(path)
    223       return self._GetTestFactory(android_mk_parser, path, build_path)
    224     else:
    225       return None
    226 
    227   def _FindUpstreamTestFactory(self, path):
    228     """Recursively searches filesystem upwards for a test factory.
    229 
    230     Args:
    231       path: file system path to search
    232 
    233     Returns:
    234       the TestFactory found or None
    235     """
    236     factory = self._GetTestFactoryForPath(path)
    237     if factory:
    238       return factory
    239     dirpath = os.path.dirname(path)
    240     if self._IsPathInBuildTree(path):
    241       return self._FindUpstreamTestFactory(dirpath)
    242     logger.Log('A tests Android.mk was not found')
    243     return None
    244 
    245   def _CreateSuites(self, android_mk_parser, path, upstream_build_path):
    246     """Creates TestSuites from a AndroidMK.
    247 
    248     Args:
    249       android_mk_parser: the AndroidMK
    250       path: absolute file system path of the makefile to evaluate
    251       upstream_build_path: the build path to use for test. This can be
    252         different than the 'path', in cases where an upstream makefile
    253         is being used.
    254 
    255     Returns:
    256       the list of tests created
    257     """
    258     factory = self._GetTestFactory(android_mk_parser, path,
    259                                    build_path=upstream_build_path)
    260     if factory:
    261       return factory.CreateTests(path)
    262     else:
    263       return []
    264